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