xref: /haiku/src/servers/app/drawing/DrawingEngine.cpp (revision 6e927a5fc080cb934e7584454f472cacf4c3e361)
1 /*
2  * Copyright 2001-2007, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  */
8 
9 #include "DrawingEngine.h"
10 
11 #include <Bitmap.h>
12 #include <stdio.h>
13 #include <algo.h>
14 #include <stack.h>
15 
16 #include "DrawState.h"
17 #include "GlyphLayoutEngine.h"
18 #include "Painter.h"
19 #include "PNGDump.h"
20 #include "ServerBitmap.h"
21 #include "ServerCursor.h"
22 #include "RenderingBuffer.h"
23 
24 #include "drawing_support.h"
25 
26 #define CRASH_IF_NOT_LOCKED
27 //#define CRASH_IF_NOT_LOCKED if (!IsParallelAccessLocked()) debugger("not parallel locked!");
28 
29 #define CRASH_IF_NOT_EXCLUSIVE_LOCKED
30 //#define CRASH_IF_NOT_EXCLUSIVE_LOCKED if (!IsExclusiveAccessLocked()) debugger("not exclusive locked!");
31 
32 // make_rect_valid
33 static inline void
34 make_rect_valid(BRect& rect)
35 {
36 	if (rect.left > rect.right) {
37 		float temp = rect.left;
38 		rect.left = rect.right;
39 		rect.right = temp;
40 	}
41 	if (rect.top > rect.bottom) {
42 		float temp = rect.top;
43 		rect.top = rect.bottom;
44 		rect.bottom = temp;
45 	}
46 }
47 
48 // extend_by_stroke_width
49 static inline void
50 extend_by_stroke_width(BRect& rect, float penSize)
51 {
52 	// "- 0.5" because if stroke width == 1, we don't need to extend
53 	float inset = -ceilf(penSize / 2.0 - 0.5);
54 	rect.InsetBy(inset, inset);
55 }
56 
57 
58 class FontLocker {
59 	public:
60 		FontLocker(const DrawState* context)
61 			:
62 			fFont(&context->Font())
63 		{
64 			fFont->Lock();
65 		}
66 
67 		FontLocker(const ServerFont* font)
68 			:
69 			fFont(font)
70 		{
71 			fFont->Lock();
72 		}
73 
74 		~FontLocker()
75 		{
76 			fFont->Unlock();
77 		}
78 
79 	private:
80 		const ServerFont*	fFont;
81 };
82 
83 
84 //	#pragma mark -
85 
86 
87 DrawingEngine::DrawingEngine(HWInterface* interface)
88 	: fPainter(new Painter()),
89 	  fGraphicsCard(NULL),
90 	  fAvailableHWAccleration(0),
91 	  fSuspendSyncLevel(0)
92 {
93 	SetHWInterface(interface);
94 }
95 
96 
97 DrawingEngine::~DrawingEngine()
98 {
99 	SetHWInterface(NULL);
100 	delete fPainter;
101 }
102 
103 
104 // #pragma mark - locking
105 
106 
107 bool
108 DrawingEngine::LockParallelAccess()
109 {
110 	return fGraphicsCard->LockParallelAccess();
111 }
112 
113 
114 void
115 DrawingEngine::UnlockParallelAccess()
116 {
117 	fGraphicsCard->UnlockParallelAccess();
118 }
119 
120 
121 bool
122 DrawingEngine::LockExclusiveAccess()
123 {
124 	return fGraphicsCard->LockExclusiveAccess();
125 }
126 
127 
128 void
129 DrawingEngine::UnlockExclusiveAccess()
130 {
131 	fGraphicsCard->UnlockExclusiveAccess();
132 }
133 
134 // #pragma mark -
135 
136 void
137 DrawingEngine::FrameBufferChanged()
138 {
139 	if (!fGraphicsCard) {
140 		fPainter->DetachFromBuffer();
141 		fAvailableHWAccleration = 0;
142 		return;
143 	}
144 
145 	// NOTE: locking is probably bogus, since we are called
146 	// in the thread that changed the frame buffer...
147 	if (LockExclusiveAccess()) {
148 		fPainter->AttachToBuffer(fGraphicsCard->DrawingBuffer());
149 		// available HW acceleration might have changed
150 		fAvailableHWAccleration = fGraphicsCard->AvailableHWAcceleration();
151 		UnlockExclusiveAccess();
152 	}
153 }
154 
155 
156 void
157 DrawingEngine::SetHWInterface(HWInterface* interface)
158 {
159 	if (fGraphicsCard == interface)
160 		return;
161 
162 	if (fGraphicsCard)
163 		fGraphicsCard->RemoveListener(this);
164 
165 	fGraphicsCard = interface;
166 
167 	if (fGraphicsCard)
168 		fGraphicsCard->AddListener(this);
169 
170 	FrameBufferChanged();
171 }
172 
173 // #pragma mark -
174 
175 //! the DrawingEngine needs to be locked!
176 void
177 DrawingEngine::ConstrainClippingRegion(const BRegion* region)
178 {
179 	CRASH_IF_NOT_LOCKED
180 
181 	fPainter->ConstrainClipping(region);
182 }
183 
184 
185 void
186 DrawingEngine::SetDrawState(const DrawState* state, int32 xOffset, int32 yOffset)
187 {
188 	fPainter->SetDrawState(state, xOffset, yOffset);
189 }
190 
191 
192 void
193 DrawingEngine::SetHighColor(const rgb_color& color)
194 {
195 	fPainter->SetHighColor(color);
196 }
197 
198 
199 void
200 DrawingEngine::SetLowColor(const rgb_color& color)
201 {
202 	fPainter->SetLowColor(color);
203 }
204 
205 
206 void
207 DrawingEngine::SetPenSize(float size)
208 {
209 	fPainter->SetPenSize(size);
210 }
211 
212 
213 void
214 DrawingEngine::SetStrokeMode(cap_mode lineCap, join_mode joinMode,
215 								float miterLimit)
216 {
217 	fPainter->SetStrokeMode(lineCap, joinMode, miterLimit);
218 }
219 
220 
221 void
222 DrawingEngine::SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc)
223 {
224 	fPainter->SetBlendingMode(srcAlpha, alphaFunc);
225 }
226 
227 
228 void
229 DrawingEngine::SetPattern(const struct pattern& pattern)
230 {
231 	fPainter->SetPattern(pattern, false);
232 }
233 
234 
235 void
236 DrawingEngine::SetDrawingMode(drawing_mode mode)
237 {
238 	fPainter->SetDrawingMode(mode);
239 }
240 
241 
242 void
243 DrawingEngine::SetFont(const ServerFont& font)
244 {
245 	fPainter->SetFont(font);
246 }
247 
248 
249 void
250 DrawingEngine::SetFont(const DrawState* state)
251 {
252 	fPainter->SetFont(state);
253 }
254 
255 
256 // #pragma mark -
257 
258 
259 void
260 DrawingEngine::SuspendAutoSync()
261 {
262 	CRASH_IF_NOT_LOCKED
263 
264 	fSuspendSyncLevel++;
265 }
266 
267 
268 void
269 DrawingEngine::Sync()
270 {
271 	CRASH_IF_NOT_LOCKED
272 
273 	fSuspendSyncLevel--;
274 	if (fSuspendSyncLevel == 0)
275 		fGraphicsCard->Sync();
276 }
277 
278 // #pragma mark -
279 
280 // CopyRegion() does a topological sort of the rects in the
281 // region. The algorithm was suggested by Ingo Weinhold.
282 // It compares each rect with each rect and builds a tree
283 // of successors so we know the order in which they can be copied.
284 // For example, let's suppose these rects are in a BRegion:
285 //                        ************
286 //                        *    B     *
287 //                        ************
288 //      *************
289 //      *           *
290 //      *     A     ****************
291 //      *           **             *
292 //      **************             *
293 //                   *     C       *
294 //                   *             *
295 //                   *             *
296 //                   ***************
297 // When copying stuff from LEFT TO RIGHT, TOP TO BOTTOM, the
298 // result of the sort will be C, A, B. For this direction, we search
299 // for the rects that have no neighbors to their right and to their
300 // bottom, These can be copied without drawing into the area of
301 // rects yet to be copied. If you move from RIGHT TO LEFT, BOTTOM TO TOP,
302 // you go look for the ones that have no neighbors to their top and left.
303 //
304 // Here I draw some rays to illustrate LEFT TO RIGHT, TOP TO BOTTOM:
305 //                        ************
306 //                        *    B     *
307 //                        ************
308 //      *************
309 //      *           *
310 //      *     A     ****************-----------------
311 //      *           **             *
312 //      **************             *
313 //                   *     C       *
314 //                   *             *
315 //                   *             *
316 //                   ***************
317 //                   |
318 //                   |
319 //                   |
320 //                   |
321 // There are no rects in the area defined by the rays to the right
322 // and bottom of rect C, so that's the one we want to copy first
323 // (for positive x and y offsets).
324 // Since A is to the left of C and B is to the top of C, The "node"
325 // for C will point to the nodes of A and B as its "successors". Therefor,
326 // A and B will have an "indegree" of 1 for C pointing to them. C will
327 // have an "indegree" of 0, because there was no rect to which C
328 // was to the left or top of. When comparing A and B, neither is left
329 // or top from the other and in the sense that the algorithm cares about.
330 
331 // NOTE: comparison of coordinates assumes that rects don't overlap
332 // and don't share the actual edge either (as is the case in BRegions).
333 
334 struct node {
335 			node()
336 			{
337 				pointers = NULL;
338 			}
339 			node(const BRect& r, int32 maxPointers)
340 			{
341 				init(r, maxPointers);
342 			}
343 			~node()
344 			{
345 				delete [] pointers;
346 			}
347 
348 	void	init(const BRect& r, int32 maxPointers)
349 			{
350 				rect = r;
351 				pointers = new node*[maxPointers];
352 				in_degree = 0;
353 				next_pointer = 0;
354 			}
355 
356 	void	push(node* node)
357 			{
358 				pointers[next_pointer] = node;
359 				next_pointer++;
360 			}
361 	node*	top()
362 			{
363 				return pointers[next_pointer];
364 			}
365 	node*	pop()
366 			{
367 				node* ret = top();
368 				next_pointer--;
369 				return ret;
370 			}
371 
372 	BRect	rect;
373 	int32	in_degree;
374 	node**	pointers;
375 	int32	next_pointer;
376 };
377 
378 static bool
379 is_left_of(const BRect& a, const BRect& b)
380 {
381 	return (a.right < b.left);
382 }
383 static bool
384 is_above(const BRect& a, const BRect& b)
385 {
386 	return (a.bottom < b.top);
387 }
388 
389 // CopyRegion
390 void
391 DrawingEngine::CopyRegion(/*const*/ BRegion* region,
392 						  int32 xOffset, int32 yOffset)
393 {
394 	CRASH_IF_NOT_EXCLUSIVE_LOCKED
395 
396 	BRect frame = region->Frame();
397 	frame = frame | frame.OffsetByCopy(xOffset, yOffset);
398 	bool cursorTouched = fGraphicsCard->HideSoftwareCursor(frame);
399 
400 	int32 count = region->CountRects();
401 
402 	// TODO: make this step unnecessary
403 	// (by using different stack impl inside node)
404 	node nodes[count];
405 	for (int32 i= 0; i < count; i++) {
406 		nodes[i].init(region->RectAt(i), count);
407 	}
408 
409 	for (int32 i = 0; i < count; i++) {
410 		BRect a = region->RectAt(i);
411 		for (int32 k = i + 1; k < count; k++) {
412 			BRect b = region->RectAt(k);
413 			int cmp = 0;
414 			// compare horizontally
415 			if (xOffset > 0) {
416 				if (is_left_of(a, b)) {
417 					cmp -= 1;
418 				} else if (is_left_of(b, a)) {
419 					cmp += 1;
420 				}
421 			} else if (xOffset < 0) {
422 				if (is_left_of(a, b)) {
423 					cmp += 1;
424 				} else if (is_left_of(b, a)) {
425 					cmp -= 1;
426 				}
427 			}
428 			// compare vertically
429 			if (yOffset > 0) {
430 				if (is_above(a, b)) {
431 					cmp -= 1;
432 				} else if (is_above(b, a)) {
433 					cmp += 1;
434 				}
435 			} else if (yOffset < 0) {
436 				if (is_above(a, b)) {
437 					cmp += 1;
438 				} else if (is_above(b, a)) {
439 					cmp -= 1;
440 				}
441 			}
442 			// add appropriate node as successor
443 			if (cmp > 0) {
444 				nodes[i].push(&nodes[k]);
445 				nodes[k].in_degree++;
446 			} else if (cmp < 0) {
447 				nodes[k].push(&nodes[i]);
448 				nodes[i].in_degree++;
449 			}
450 		}
451 	}
452 	// put all nodes onto a stack that have an "indegree" count of zero
453 	stack<node*> inDegreeZeroNodes;
454 	for (int32 i = 0; i < count; i++) {
455 		if (nodes[i].in_degree == 0) {
456 			inDegreeZeroNodes.push(&nodes[i]);
457 		}
458 	}
459 	// pop the rects from the stack, do the actual copy operation
460 	// and decrease the "indegree" count of the other rects not
461 	// currently on the stack and to which the current rect pointed
462 	// to. If their "indegree" count reaches zero, put them onto the
463 	// stack as well.
464 
465 	clipping_rect* sortedRectList = NULL;
466 	int32 nextSortedIndex = 0;
467 
468 	if (fAvailableHWAccleration & HW_ACC_COPY_REGION)
469 		sortedRectList = new clipping_rect[count];
470 
471 	while (!inDegreeZeroNodes.empty()) {
472 		node* n = inDegreeZeroNodes.top();
473 		inDegreeZeroNodes.pop();
474 
475 		// do the software implementation or add to sorted
476 		// rect list for using the HW accelerated version
477 		// later
478 		if (sortedRectList) {
479 			sortedRectList[nextSortedIndex].left	= (int32)n->rect.left;
480 			sortedRectList[nextSortedIndex].top		= (int32)n->rect.top;
481 			sortedRectList[nextSortedIndex].right	= (int32)n->rect.right;
482 			sortedRectList[nextSortedIndex].bottom	= (int32)n->rect.bottom;
483 			nextSortedIndex++;
484 		} else {
485 			BRect touched = _CopyRect(n->rect, xOffset, yOffset);
486 			fGraphicsCard->Invalidate(touched);
487 		}
488 
489 		for (int32 k = 0; k < n->next_pointer; k++) {
490 			n->pointers[k]->in_degree--;
491 			if (n->pointers[k]->in_degree == 0)
492 				inDegreeZeroNodes.push(n->pointers[k]);
493 		}
494 	}
495 
496 	// trigger the HW accelerated version if is was available
497 	if (sortedRectList)
498 		fGraphicsCard->CopyRegion(sortedRectList, count, xOffset, yOffset);
499 
500 	delete[] sortedRectList;
501 
502 	if (cursorTouched)
503 		fGraphicsCard->ShowSoftwareCursor();
504 }
505 
506 // InvertRect
507 void
508 DrawingEngine::InvertRect(BRect r)
509 {
510 	CRASH_IF_NOT_LOCKED
511 
512 	make_rect_valid(r);
513 	r = fPainter->ClipRect(r);
514 	if (r.IsValid()) {
515 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r);
516 
517 		// try hardware optimized version first
518 		if (fAvailableHWAccleration & HW_ACC_INVERT_REGION) {
519 			BRegion region(r);
520 			region.IntersectWith(fPainter->ClippingRegion());
521 			fGraphicsCard->InvertRegion(region);
522 		} else {
523 			fPainter->InvertRect(r);
524 
525 			fGraphicsCard->Invalidate(r);
526 		}
527 
528 		if (cursorTouched)
529 			fGraphicsCard->ShowSoftwareCursor();
530 	}
531 }
532 
533 // DrawBitmap
534 void
535 DrawingEngine::DrawBitmap(ServerBitmap *bitmap,
536 						  const BRect &source, const BRect &dest)
537 {
538 	CRASH_IF_NOT_LOCKED
539 
540 	BRect clipped = fPainter->ClipRect(dest);
541 	if (clipped.IsValid()) {
542 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped);
543 
544 		fPainter->DrawBitmap(bitmap, source, dest);
545 
546 		fGraphicsCard->Invalidate(clipped);
547 		if (cursorTouched)
548 			fGraphicsCard->ShowSoftwareCursor();
549 	}
550 }
551 
552 // DrawArc
553 void
554 DrawingEngine::DrawArc(BRect r, const float &angle,
555 					   const float &span, bool filled)
556 {
557 	CRASH_IF_NOT_LOCKED
558 
559 	make_rect_valid(r);
560 	BRect clipped(r);
561 	if (!filled)
562 		extend_by_stroke_width(clipped, fPainter->PenSize());
563 	clipped = fPainter->ClipRect(r);
564 	if (clipped.IsValid()) {
565 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped);
566 
567 		float xRadius = r.Width() / 2.0;
568 		float yRadius = r.Height() / 2.0;
569 		BPoint center(r.left + xRadius,
570 					  r.top + yRadius);
571 
572 		if (filled)
573 			fPainter->FillArc(center, xRadius, yRadius, angle, span);
574 		else
575 			fPainter->StrokeArc(center, xRadius, yRadius, angle, span);
576 
577 		fGraphicsCard->Invalidate(clipped);
578 		if (cursorTouched)
579 			fGraphicsCard->ShowSoftwareCursor();
580 	}
581 }
582 
583 // DrawBezier
584 void
585 DrawingEngine::DrawBezier(BPoint *pts, bool filled)
586 {
587 	CRASH_IF_NOT_LOCKED
588 
589 	// TODO: figure out bounds and hide cursor depending on that
590 	fGraphicsCard->HideSoftwareCursor();
591 
592 	BRect touched = fPainter->DrawBezier(pts, filled);
593 
594 	fGraphicsCard->Invalidate(touched);
595 	fGraphicsCard->ShowSoftwareCursor();
596 }
597 
598 // DrawEllipse
599 void
600 DrawingEngine::DrawEllipse(BRect r, bool filled)
601 {
602 	CRASH_IF_NOT_LOCKED
603 
604 	make_rect_valid(r);
605 	BRect clipped = r;
606 	fPainter->AlignEllipseRect(&clipped, filled);
607 
608 	if (!filled)
609 		extend_by_stroke_width(clipped, fPainter->PenSize());
610 
611 	clipped.left = floorf(clipped.left);
612 	clipped.top = floorf(clipped.top);
613 	clipped.right = ceilf(clipped.right);
614 	clipped.bottom = ceilf(clipped.bottom);
615 
616 	clipped = fPainter->ClipRect(clipped);
617 
618 	if (clipped.IsValid()) {
619 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped);
620 
621 		fPainter->DrawEllipse(r, filled);
622 
623 		fGraphicsCard->Invalidate(clipped);
624 		if (cursorTouched)
625 			fGraphicsCard->ShowSoftwareCursor();
626 	}
627 }
628 
629 // DrawPolygon
630 void
631 DrawingEngine::DrawPolygon(BPoint* ptlist, int32 numpts,
632 						   BRect bounds, bool filled, bool closed)
633 {
634 	CRASH_IF_NOT_LOCKED
635 
636 	make_rect_valid(bounds);
637 	if (!filled)
638 		extend_by_stroke_width(bounds, fPainter->PenSize());
639 	bounds = fPainter->ClipRect(bounds);
640 	if (bounds.IsValid()) {
641 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(bounds);
642 
643 		fPainter->DrawPolygon(ptlist, numpts, filled, closed);
644 
645 		fGraphicsCard->Invalidate(bounds);
646 		if (cursorTouched)
647 			fGraphicsCard->ShowSoftwareCursor();
648 	}
649 }
650 
651 // #pragma mark - rgb_color
652 
653 void
654 DrawingEngine::StrokePoint(const BPoint& pt, const rgb_color &color)
655 {
656 	StrokeLine(pt, pt, color);
657 }
658 
659 // StrokeLine
660 //
661 // * this function is only used by Decorators
662 // * it assumes a one pixel wide line
663 void
664 DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end,
665 						  const rgb_color &color)
666 {
667 	CRASH_IF_NOT_LOCKED
668 
669 	BRect touched(start, end);
670 	make_rect_valid(touched);
671 	touched = fPainter->ClipRect(touched);
672 	bool cursorTouched = fGraphicsCard->HideSoftwareCursor(touched);
673 
674 	if (!fPainter->StraightLine(start, end, color)) {
675 		fPainter->SetHighColor(color);
676 		fPainter->SetDrawingMode(B_OP_OVER);
677 		fPainter->StrokeLine(start, end);
678 	}
679 
680 	fGraphicsCard->Invalidate(touched);
681 	if (cursorTouched)
682 		fGraphicsCard->ShowSoftwareCursor();
683 }
684 
685 // this function is used to draw a one pixel wide rect
686 void
687 DrawingEngine::StrokeRect(BRect r, const rgb_color &color)
688 {
689 	CRASH_IF_NOT_LOCKED
690 
691 	make_rect_valid(r);
692 	BRect clipped = fPainter->ClipRect(r);
693 	if (clipped.IsValid()) {
694 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped);
695 
696 		fPainter->StrokeRect(r, color);
697 
698 		fGraphicsCard->Invalidate(clipped);
699 		if (cursorTouched)
700 			fGraphicsCard->ShowSoftwareCursor();
701 	}
702 }
703 
704 
705 void
706 DrawingEngine::FillRect(BRect r, const rgb_color& color)
707 {
708 	CRASH_IF_NOT_LOCKED
709 
710 	// NOTE: Write locking because we might use HW acceleration.
711 	// This needs to be investigated, I'm doing this because of
712 	// gut feeling.
713 	make_rect_valid(r);
714 	r = fPainter->ClipRect(r);
715 	if (r.IsValid()) {
716 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r);
717 
718 		// try hardware optimized version first
719 		if (fAvailableHWAccleration & HW_ACC_FILL_REGION) {
720 			BRegion region(r);
721 			region.IntersectWith(fPainter->ClippingRegion());
722 			fGraphicsCard->FillRegion(region, color,
723 									  fSuspendSyncLevel == 0
724 									  || cursorTouched);
725 		} else {
726 			fPainter->FillRect(r, color);
727 
728 			fGraphicsCard->Invalidate(r);
729 		}
730 
731 		if (cursorTouched)
732 			fGraphicsCard->ShowSoftwareCursor();
733 	}
734 }
735 
736 
737 void
738 DrawingEngine::FillRegion(BRegion& r, const rgb_color& color)
739 {
740 	CRASH_IF_NOT_LOCKED
741 
742 	// NOTE: region expected to be already clipped correctly!!
743 	BRect frame = r.Frame();
744 	if (!fPainter->Bounds().Contains(frame)) {
745 		// NOTE: I am not quite sure yet how this can happen, but appearantly it can (see bug 634)
746 		// This function is used for internal app_server painting, in the case of bug 634,
747 		// the background of views is painted. But the view region should never be outside the
748 		// frame buffer bounds.
749 //		char message[1024];
750 //		BRect bounds = fPainter->Bounds();
751 //		sprintf(message, "FillRegion() - painter: (%d, %d)->(%d, %d), region: (%d, %d)->(%d, %d)",
752 //			(int)bounds.left, (int)bounds.top, (int)bounds.right, (int)bounds.bottom,
753 //			(int)frame.left, (int)frame.top, (int)frame.right, (int)frame.bottom);
754 //		debugger(message);
755 		return;
756 	}
757 
758 	bool cursorTouched = fGraphicsCard->HideSoftwareCursor(frame);
759 
760 	// try hardware optimized version first
761 	if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0
762 		&& frame.Width() * frame.Height() > 100) {
763 		fGraphicsCard->FillRegion(r, color, fSuspendSyncLevel == 0
764 											|| cursorTouched);
765 	} else {
766 		int32 count = r.CountRects();
767 		for (int32 i = 0; i < count; i++) {
768 			fPainter->FillRectNoClipping(r.RectAtInt(i), color);
769 		}
770 
771 		fGraphicsCard->Invalidate(frame);
772 	}
773 
774 	if (cursorTouched)
775 		fGraphicsCard->ShowSoftwareCursor();
776 }
777 
778 // #pragma mark - DrawState
779 
780 void
781 DrawingEngine::StrokeRect(BRect r)
782 {
783 	CRASH_IF_NOT_LOCKED
784 
785 	// support invalid rects
786 	make_rect_valid(r);
787 	BRect clipped(r);
788 	extend_by_stroke_width(clipped, fPainter->PenSize());
789 	clipped = fPainter->ClipRect(clipped);
790 	if (clipped.IsValid()) {
791 
792 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped);
793 
794 		fPainter->StrokeRect(r);
795 
796 		fGraphicsCard->Invalidate(clipped);
797 		if (cursorTouched)
798 			fGraphicsCard->ShowSoftwareCursor();
799 	}
800 }
801 
802 
803 void
804 DrawingEngine::FillRect(BRect r)
805 {
806 	CRASH_IF_NOT_LOCKED
807 
808 	make_rect_valid(r);
809 	r = fPainter->AlignAndClipRect(r);
810 	if (r.IsValid()) {
811 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r);
812 
813 		bool doInSoftware = true;
814 		if ((r.Width() + 1) * (r.Height() + 1) > 100.0) {
815 			// try hardware optimized version first
816 			// if the rect is large enough
817 			if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) {
818 				if (fPainter->Pattern() == B_SOLID_HIGH
819 					&& (fPainter->DrawingMode() == B_OP_COPY
820 						|| fPainter->DrawingMode() == B_OP_OVER)) {
821 					BRegion region(r);
822 					region.IntersectWith(fPainter->ClippingRegion());
823 					fGraphicsCard->FillRegion(region, fPainter->HighColor(),
824 											  fSuspendSyncLevel == 0
825 											  || cursorTouched);
826 					doInSoftware = false;
827 				} else if (fPainter->Pattern() == B_SOLID_LOW
828 						&& fPainter->DrawingMode() == B_OP_COPY) {
829 					BRegion region(r);
830 					region.IntersectWith(fPainter->ClippingRegion());
831 					fGraphicsCard->FillRegion(region, fPainter->LowColor(),
832 											  fSuspendSyncLevel == 0
833 											  || cursorTouched);
834 					doInSoftware = false;
835 				}
836 			}
837 		}
838 
839 		if (doInSoftware && fAvailableHWAccleration & HW_ACC_INVERT_REGION
840 				&& fPainter->Pattern() == B_SOLID_HIGH
841 				&& fPainter->DrawingMode() == B_OP_INVERT) {
842 			BRegion region(r);
843 			region.IntersectWith(fPainter->ClippingRegion());
844 			fGraphicsCard->InvertRegion(region);
845 			doInSoftware = false;
846 		}
847 
848 		if (doInSoftware) {
849 			fPainter->FillRect(r);
850 
851 			fGraphicsCard->Invalidate(r);
852 		}
853 
854 		if (cursorTouched)
855 			fGraphicsCard->ShowSoftwareCursor();
856 	}
857 }
858 
859 
860 void
861 DrawingEngine::FillRegion(BRegion& r)
862 {
863 	CRASH_IF_NOT_LOCKED
864 
865 	BRect clipped = fPainter->ClipRect(r.Frame());
866 	if (clipped.IsValid()) {
867 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped);
868 
869 		bool doInSoftware = true;
870 		// try hardware optimized version first
871 		if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) {
872 			if (fPainter->Pattern() == B_SOLID_HIGH
873 				&& (fPainter->DrawingMode() == B_OP_COPY
874 					|| fPainter->DrawingMode() == B_OP_OVER)) {
875 				r.IntersectWith(fPainter->ClippingRegion());
876 				fGraphicsCard->FillRegion(r, fPainter->HighColor(),
877 										  fSuspendSyncLevel == 0
878 										  || cursorTouched);
879 				doInSoftware = false;
880 			} else if (fPainter->Pattern() == B_SOLID_LOW
881 					&& fPainter->DrawingMode() == B_OP_COPY) {
882 				r.IntersectWith(fPainter->ClippingRegion());
883 				fGraphicsCard->FillRegion(r, fPainter->LowColor(),
884 										  fSuspendSyncLevel == 0
885 										  || cursorTouched);
886 				doInSoftware = false;
887 			}
888 		}
889 
890 		if (doInSoftware && fAvailableHWAccleration & HW_ACC_INVERT_REGION
891 				&& fPainter->Pattern() == B_SOLID_HIGH
892 				&& fPainter->DrawingMode() == B_OP_INVERT) {
893 			r.IntersectWith(fPainter->ClippingRegion());
894 			fGraphicsCard->InvertRegion(r);
895 			doInSoftware = false;
896 		}
897 
898 		if (doInSoftware) {
899 
900 			BRect touched = fPainter->FillRect(r.RectAt(0));
901 
902 			int32 count = r.CountRects();
903 			for (int32 i = 1; i < count; i++) {
904 				touched = touched | fPainter->FillRect(r.RectAt(i));
905 			}
906 
907 			fGraphicsCard->Invalidate(touched);
908 		}
909 
910 		if (cursorTouched)
911 			fGraphicsCard->ShowSoftwareCursor();
912 	}
913 }
914 
915 
916 void
917 DrawingEngine::DrawRoundRect(BRect r, float xrad, float yrad, bool filled)
918 {
919 	CRASH_IF_NOT_LOCKED
920 
921 	// NOTE: the stroke does not extend past "r" in R5,
922 	// though I consider this unexpected behaviour.
923 	make_rect_valid(r);
924 	BRect clipped = fPainter->ClipRect(r);
925 
926 	clipped.left = floorf(clipped.left);
927 	clipped.top = floorf(clipped.top);
928 	clipped.right = ceilf(clipped.right);
929 	clipped.bottom = ceilf(clipped.bottom);
930 
931 	if (clipped.IsValid()) {
932 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped);
933 
934 		BRect touched = filled ? fPainter->FillRoundRect(r, xrad, yrad)
935 							   : fPainter->StrokeRoundRect(r, xrad, yrad);
936 
937 		fGraphicsCard->Invalidate(touched);
938 		if (cursorTouched)
939 			fGraphicsCard->ShowSoftwareCursor();
940 	}
941 }
942 
943 
944 void
945 DrawingEngine::DrawShape(const BRect& bounds, int32 opCount,
946 	const uint32* opList, int32 ptCount, const BPoint* ptList, bool filled)
947 {
948 	CRASH_IF_NOT_LOCKED
949 
950 	// NOTE: hides cursor regardless of if and where
951 	// shape is drawn on screen, TODO: optimize
952 	fGraphicsCard->HideSoftwareCursor();
953 
954 	BRect touched = fPainter->DrawShape(opCount, opList,
955 										ptCount, ptList,
956 										filled);
957 
958 	fGraphicsCard->Invalidate(touched);
959 	fGraphicsCard->ShowSoftwareCursor();
960 }
961 
962 
963 void
964 DrawingEngine::DrawTriangle(BPoint* pts, const BRect& bounds, bool filled)
965 {
966 	CRASH_IF_NOT_LOCKED
967 
968 	BRect clipped(bounds);
969 	if (!filled)
970 		extend_by_stroke_width(clipped, fPainter->PenSize());
971 	clipped = fPainter->ClipRect(clipped);
972 	if (clipped.IsValid()) {
973 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped);
974 
975 		if (filled)
976 			fPainter->FillTriangle(pts[0], pts[1], pts[2]);
977 		else
978 			fPainter->StrokeTriangle(pts[0], pts[1], pts[2]);
979 
980 		fGraphicsCard->Invalidate(clipped);
981 		if (cursorTouched)
982 			fGraphicsCard->ShowSoftwareCursor();
983 	}
984 }
985 
986 // StrokeLine
987 void
988 DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end)
989 {
990 	CRASH_IF_NOT_LOCKED
991 
992 	BRect touched(start, end);
993 	make_rect_valid(touched);
994 	extend_by_stroke_width(touched, fPainter->PenSize());
995 	touched = fPainter->ClipRect(touched);
996 	if (touched.IsValid()) {
997 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(touched);
998 
999 		fPainter->StrokeLine(start, end);
1000 
1001 		fGraphicsCard->Invalidate(touched);
1002 		if (cursorTouched)
1003 			fGraphicsCard->ShowSoftwareCursor();
1004 	}
1005 }
1006 
1007 // StrokeLineArray
1008 void
1009 DrawingEngine::StrokeLineArray(int32 numLines,
1010 	const LineArrayData *linedata)
1011 {
1012 	CRASH_IF_NOT_LOCKED
1013 
1014 	if (!linedata || numLines <= 0)
1015 		return;
1016 
1017 	// figure out bounding box for line array
1018 	const LineArrayData *data = (const LineArrayData *)&(linedata[0]);
1019 	BRect touched(min_c(data->pt1.x, data->pt2.x),
1020 				  min_c(data->pt1.y, data->pt2.y),
1021 				  max_c(data->pt1.x, data->pt2.x),
1022 				  max_c(data->pt1.y, data->pt2.y));
1023 
1024 	for (int32 i = 1; i < numLines; i++) {
1025 		data = (const LineArrayData *)&(linedata[i]);
1026 		BRect box(min_c(data->pt1.x, data->pt2.x),
1027 				  min_c(data->pt1.y, data->pt2.y),
1028 				  max_c(data->pt1.x, data->pt2.x),
1029 				  max_c(data->pt1.y, data->pt2.y));
1030 		touched = touched | box;
1031 	}
1032 	extend_by_stroke_width(touched, fPainter->PenSize());
1033 	touched = fPainter->ClipRect(touched);
1034 	if (touched.IsValid()) {
1035 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(touched);
1036 
1037 		data = (const LineArrayData *)&(linedata[0]);
1038 
1039 		// store current graphics state, we mess with the
1040 		// high color and pattern...
1041 		rgb_color oldColor = fPainter->HighColor();
1042 		struct pattern pattern = fPainter->Pattern();
1043 
1044 		fPainter->SetHighColor(data->color);
1045 		fPainter->SetPattern(B_SOLID_HIGH);
1046 		fPainter->StrokeLine(data->pt1, data->pt2);
1047 
1048 		for (int32 i = 1; i < numLines; i++) {
1049 			data = (const LineArrayData *)&(linedata[i]);
1050 			fPainter->SetHighColor(data->color);
1051 			fPainter->StrokeLine(data->pt1, data->pt2);
1052 		}
1053 
1054 		// restore correct drawing state highcolor and pattern
1055 		fPainter->SetHighColor(oldColor);
1056 		fPainter->SetPattern(pattern);
1057 
1058 		fGraphicsCard->Invalidate(touched);
1059 		if (cursorTouched)
1060 			fGraphicsCard->ShowSoftwareCursor();
1061 	}
1062 }
1063 
1064 // #pragma mark -
1065 
1066 BPoint
1067 DrawingEngine::DrawString(const char* string, int32 length,
1068 						  const BPoint& pt, escapement_delta* delta)
1069 {
1070 	CRASH_IF_NOT_LOCKED
1071 
1072 	BPoint penLocation = pt;
1073 
1074 	// try a fast clipping path
1075 	float fontSize = fPainter->Font().Size();
1076 	BRect clippingFrame = fPainter->ClippingRegion()->Frame();
1077 	if (pt.x > clippingFrame.right || pt.y + fontSize < clippingFrame.top
1078 		|| pt.y - fontSize > clippingFrame.bottom) {
1079 		penLocation.x += StringWidth(string, length, delta);
1080 		return penLocation;
1081 	}
1082 
1083 	// use a FontCacheRefernece to speed up the second pass of
1084 	// drawing the string
1085 	FontCacheReference cacheReference;
1086 
1087 //bigtime_t now = system_time();
1088 // TODO: BoundingBox is quite slow!! Optimizing it will be beneficial.
1089 // Cursiously, the DrawString after it is actually faster!?!
1090 // TODO: make the availability of the hardware cursor part of the
1091 // HW acceleration flags and skip all calculations for HideSoftwareCursor
1092 // in case we don't have one.
1093 // TODO: Watch out about penLocation and use Painter::PenLocation() when
1094 // not using BoundindBox anymore.
1095 	BRect b = fPainter->BoundingBox(string, length, pt, &penLocation, delta,
1096 		&cacheReference);
1097 	// stop here if we're supposed to render outside of the clipping
1098 	b = fPainter->ClipRect(b);
1099 	if (b.IsValid()) {
1100 //printf("bounding box '%s': %lld µs\n", string, system_time() - now);
1101 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(b);
1102 
1103 //now = system_time();
1104 		BRect touched = fPainter->DrawString(string, length, pt, delta,
1105 			&cacheReference);
1106 //printf("drawing string: %lld µs\n", system_time() - now);
1107 
1108 		fGraphicsCard->Invalidate(touched);
1109 		if (cursorTouched)
1110 			fGraphicsCard->ShowSoftwareCursor();
1111 	}
1112 
1113 	return penLocation;
1114 }
1115 
1116 // StringWidth
1117 float
1118 DrawingEngine::StringWidth(const char* string, int32 length,
1119 						   escapement_delta* delta)
1120 {
1121 	return fPainter->StringWidth(string, length, delta);
1122 }
1123 
1124 // StringWidth
1125 float
1126 DrawingEngine::StringWidth(const char* string, int32 length,
1127 	const ServerFont& font, escapement_delta* delta)
1128 {
1129 	return font.StringWidth(string, length, delta);
1130 }
1131 
1132 // #pragma mark -
1133 
1134 // DumpToFile
1135 bool
1136 DrawingEngine::DumpToFile(const char *path)
1137 {
1138 	CRASH_IF_NOT_EXCLUSIVE_LOCKED
1139 
1140 	RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer();
1141 	if (buffer) {
1142 		BRect bounds(0.0, 0.0, buffer->Width() - 1, buffer->Height() - 1);
1143 		SaveToPNG(path, bounds, buffer->ColorSpace(),
1144 				  buffer->Bits(),
1145 				  buffer->BitsLength(),
1146 				  buffer->BytesPerRow());
1147 		return true;
1148 	}
1149 	return false;
1150 }
1151 
1152 // DumpToBitmap
1153 ServerBitmap*
1154 DrawingEngine::DumpToBitmap()
1155 {
1156 	return NULL;
1157 }
1158 
1159 status_t
1160 DrawingEngine::ReadBitmap(ServerBitmap *bitmap, bool drawCursor, BRect bounds)
1161 {
1162 	CRASH_IF_NOT_EXCLUSIVE_LOCKED
1163 
1164 	RenderingBuffer *buffer = fGraphicsCard->DrawingBuffer();
1165 	if (!buffer)
1166 		return B_ERROR;
1167 
1168 	BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1);
1169 	bounds = bounds & clip;
1170 	fGraphicsCard->HideSoftwareCursor(bounds);
1171 
1172 	status_t result = bitmap->ImportBits(buffer->Bits(), buffer->BitsLength(),
1173 		buffer->BytesPerRow(), buffer->ColorSpace(),
1174 		bounds.LeftTop(), BPoint(0, 0),
1175 		bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1);
1176 
1177 	if (drawCursor) {
1178 		ServerCursor *cursor = fGraphicsCard->Cursor();
1179 		int32 cursorWidth = cursor->Width();
1180 		int32 cursorHeight = cursor->Height();
1181 
1182 		BPoint cursorPosition = fGraphicsCard->CursorPosition();
1183 		cursorPosition -= bounds.LeftTop() + cursor->GetHotSpot();
1184 
1185 		BBitmap cursorArea(BRect(0, 0, cursorWidth - 1, cursorHeight - 1),
1186 			B_BITMAP_NO_SERVER_LINK, B_RGBA32);
1187 
1188 		cursorArea.ImportBits(bitmap->Bits(), bitmap->BitsLength(),
1189 			bitmap->BytesPerRow(), bitmap->ColorSpace(),
1190 			cursorPosition,	BPoint(0, 0),
1191 			cursorWidth, cursorHeight);
1192 
1193 		uint8 *bits = (uint8 *)cursorArea.Bits();
1194 		uint8 *cursorBits = (uint8 *)cursor->Bits();
1195 		for (int32 i = 0; i < cursorHeight; i++) {
1196 			for (int32 j = 0; j < cursorWidth; j++) {
1197 				uint8 alpha = 255 - cursorBits[3];
1198 				bits[0] = ((bits[0] * alpha) >> 8) + cursorBits[0];
1199 				bits[1] = ((bits[1] * alpha) >> 8) + cursorBits[1];
1200 				bits[2] = ((bits[2] * alpha) >> 8) + cursorBits[2];
1201 				cursorBits += 4;
1202 				bits += 4;
1203 			}
1204 		}
1205 
1206 		bitmap->ImportBits(cursorArea.Bits(), cursorArea.BitsLength(),
1207 			cursorArea.BytesPerRow(), cursorArea.ColorSpace(),
1208 			BPoint(0, 0), cursorPosition,
1209 			cursorWidth, cursorHeight);
1210 	}
1211 
1212 	fGraphicsCard->ShowSoftwareCursor();
1213 
1214 	return result;
1215 }
1216 
1217 // #pragma mark -
1218 
1219 BRect
1220 DrawingEngine::_CopyRect(BRect src, int32 xOffset, int32 yOffset) const
1221 {
1222 	// TODO: assumes drawing buffer is 32 bits (which it currently always is)
1223 	BRect dst;
1224 	RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer();
1225 	if (buffer) {
1226 		BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1);
1227 
1228 		dst = src;
1229 		dst.OffsetBy(xOffset, yOffset);
1230 
1231 		if (clip.Intersects(src) && clip.Intersects(dst)) {
1232 			uint32 bytesPerRow = buffer->BytesPerRow();
1233 			uint8* bits = (uint8*)buffer->Bits();
1234 
1235 			// clip source rect
1236 			src = src & clip;
1237 			// clip dest rect
1238 			dst = dst & clip;
1239 			// move dest back over source and clip source to dest
1240 			dst.OffsetBy(-xOffset, -yOffset);
1241 			src = src & dst;
1242 
1243 			// calc offset in buffer
1244 			bits += (int32)src.left * 4 + (int32)src.top * bytesPerRow;
1245 
1246 			uint32 width = src.IntegerWidth() + 1;
1247 			uint32 height = src.IntegerHeight() + 1;
1248 
1249 			_CopyRect(bits, width, height, bytesPerRow, xOffset, yOffset);
1250 
1251 			// offset dest again, because it is return value
1252 			dst.OffsetBy(xOffset, yOffset);
1253 		}
1254 	}
1255 	return dst;
1256 }
1257 
1258 
1259 void
1260 DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height,
1261 	uint32 bytesPerRow, int32 xOffset, int32 yOffset) const
1262 {
1263 	// TODO: assumes drawing buffer is 32 bits (which it currently always is)
1264 	int32 xIncrement;
1265 	int32 yIncrement;
1266 
1267 	if (yOffset == 0 && xOffset > 0) {
1268 		// copy from right to left
1269 		xIncrement = -1;
1270 		src += (width - 1) * 4;
1271 	} else {
1272 		// copy from left to right
1273 		xIncrement = 1;
1274 	}
1275 
1276 	if (yOffset > 0) {
1277 		// copy from bottom to top
1278 		yIncrement = -bytesPerRow;
1279 		src += (height - 1) * bytesPerRow;
1280 	} else {
1281 		// copy from top to bottom
1282 		yIncrement = bytesPerRow;
1283 	}
1284 
1285 	uint8* dst = src + yOffset * bytesPerRow + xOffset * 4;
1286 
1287 	if (xIncrement == 1) {
1288 		uint8 tmpBuffer[width * 4];
1289 		for (uint32 y = 0; y < height; y++) {
1290 			// NOTE: read into temporary scanline buffer,
1291 			// avoid memcpy because it might be graphics card memory
1292 			gfxcpy32(tmpBuffer, src, width * 4);
1293 			// write back temporary scanline buffer
1294 			// NOTE: **don't read and write over the PCI bus
1295 			// at the same time**
1296 			memcpy(dst, tmpBuffer, width * 4);
1297 // NOTE: this (instead of the two pass copy above) might
1298 // speed up QEMU -> ?!? (would depend on how it emulates
1299 // the PCI bus...)
1300 // TODO: would be nice if we actually knew
1301 // if we're operating in graphics memory or main memory...
1302 //memcpy(dst, src, width * 4);
1303 			src += yIncrement;
1304 			dst += yIncrement;
1305 		}
1306 	} else {
1307 		for (uint32 y = 0; y < height; y++) {
1308 			uint32* srcHandle = (uint32*)src;
1309 			uint32* dstHandle = (uint32*)dst;
1310 			for (uint32 x = 0; x < width; x++) {
1311 				*dstHandle = *srcHandle;
1312 				srcHandle += xIncrement;
1313 				dstHandle += xIncrement;
1314 			}
1315 			src += yIncrement;
1316 			dst += yIncrement;
1317 		}
1318 	}
1319 }
1320 
1321 
1322