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