xref: /haiku/src/servers/app/drawing/DrawingEngine.cpp (revision f75a7bf508f3156d63a14f8fd77c5e0ca4d08c42)
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 		if (fGraphicsCard->IsDoubleBuffered())
532 			fGraphicsCard->Invalidate(region->Frame());
533 	}
534 
535 	delete[] sortedRectList;
536 }
537 
538 // InvertRect
539 void
540 DrawingEngine::InvertRect(BRect r)
541 {
542 	CRASH_IF_NOT_LOCKED
543 
544 	make_rect_valid(r);
545 	r = fPainter->ClipRect(r);
546 	if (!r.IsValid())
547 		return;
548 
549 	AutoFloatingOverlaysHider _(fGraphicsCard, r);
550 
551 	// try hardware optimized version first
552 	if (fAvailableHWAccleration & HW_ACC_INVERT_REGION) {
553 		BRegion region(r);
554 		region.IntersectWith(fPainter->ClippingRegion());
555 		fGraphicsCard->InvertRegion(region);
556 		if (fGraphicsCard->IsDoubleBuffered())
557 			_CopyToFront(r);
558 	} else {
559 		fPainter->InvertRect(r);
560 		_CopyToFront(r);
561 	}
562 }
563 
564 // DrawBitmap
565 void
566 DrawingEngine::DrawBitmap(ServerBitmap* bitmap, const BRect& bitmapRect,
567 	const BRect& viewRect, uint32 options)
568 {
569 	CRASH_IF_NOT_LOCKED
570 
571 	BRect clipped = fPainter->ClipRect(viewRect);
572 	if (clipped.IsValid()) {
573 		AutoFloatingOverlaysHider _(fGraphicsCard, clipped);
574 
575 		fPainter->DrawBitmap(bitmap, bitmapRect, viewRect, options);
576 
577 		_CopyToFront(clipped);
578 	}
579 }
580 
581 // DrawArc
582 void
583 DrawingEngine::DrawArc(BRect r, const float& angle, const float& span,
584 	bool filled)
585 {
586 	CRASH_IF_NOT_LOCKED
587 
588 	make_rect_valid(r);
589 	fPainter->AlignEllipseRect(&r, filled);
590 	BRect clipped(r);
591 
592 	if (!filled)
593 		extend_by_stroke_width(clipped, fPainter->PenSize());
594 
595 	clipped = fPainter->ClipRect(r);
596 
597 	if (clipped.IsValid()) {
598 		AutoFloatingOverlaysHider _(fGraphicsCard, clipped);
599 
600 		float xRadius = r.Width() / 2.0;
601 		float yRadius = r.Height() / 2.0;
602 		BPoint center(r.left + xRadius,
603 					  r.top + yRadius);
604 
605 		if (filled)
606 			fPainter->FillArc(center, xRadius, yRadius, angle, span);
607 		else
608 			fPainter->StrokeArc(center, xRadius, yRadius, angle, span);
609 
610 		_CopyToFront(clipped);
611 	}
612 }
613 
614 // DrawBezier
615 void
616 DrawingEngine::DrawBezier(BPoint* pts, bool filled)
617 {
618 	CRASH_IF_NOT_LOCKED
619 
620 	// TODO: figure out bounds and hide cursor depending on that
621 	AutoFloatingOverlaysHider _(fGraphicsCard);
622 
623 	BRect touched = fPainter->DrawBezier(pts, filled);
624 
625 	_CopyToFront(touched);
626 }
627 
628 // DrawEllipse
629 void
630 DrawingEngine::DrawEllipse(BRect r, bool filled)
631 {
632 	CRASH_IF_NOT_LOCKED
633 
634 	make_rect_valid(r);
635 	BRect clipped = r;
636 	fPainter->AlignEllipseRect(&clipped, filled);
637 
638 	if (!filled)
639 		extend_by_stroke_width(clipped, fPainter->PenSize());
640 
641 	clipped.left = floorf(clipped.left);
642 	clipped.top = floorf(clipped.top);
643 	clipped.right = ceilf(clipped.right);
644 	clipped.bottom = ceilf(clipped.bottom);
645 
646 	clipped = fPainter->ClipRect(clipped);
647 
648 	if (clipped.IsValid()) {
649 		AutoFloatingOverlaysHider _(fGraphicsCard, clipped);
650 
651 		fPainter->DrawEllipse(r, filled);
652 
653 		_CopyToFront(clipped);
654 	}
655 }
656 
657 // DrawPolygon
658 void
659 DrawingEngine::DrawPolygon(BPoint* ptlist, int32 numpts, BRect bounds,
660 	bool filled, bool closed)
661 {
662 	CRASH_IF_NOT_LOCKED
663 
664 	make_rect_valid(bounds);
665 	if (!filled)
666 		extend_by_stroke_width(bounds, fPainter->PenSize());
667 	bounds = fPainter->ClipRect(bounds);
668 	if (bounds.IsValid()) {
669 		AutoFloatingOverlaysHider _(fGraphicsCard, bounds);
670 
671 		fPainter->DrawPolygon(ptlist, numpts, filled, closed);
672 
673 		_CopyToFront(bounds);
674 	}
675 }
676 
677 // #pragma mark - rgb_color
678 
679 void
680 DrawingEngine::StrokePoint(const BPoint& pt, const rgb_color& color)
681 {
682 	StrokeLine(pt, pt, color);
683 }
684 
685 // StrokeLine
686 //
687 // * this function is only used by Decorators
688 // * it assumes a one pixel wide line
689 void
690 DrawingEngine::StrokeLine(const BPoint& start, const BPoint& end,
691 	const rgb_color& color)
692 {
693 	CRASH_IF_NOT_LOCKED
694 
695 	BRect touched(start, end);
696 	make_rect_valid(touched);
697 	touched = fPainter->ClipRect(touched);
698 	AutoFloatingOverlaysHider _(fGraphicsCard, touched);
699 
700 	if (!fPainter->StraightLine(start, end, color)) {
701 		rgb_color previousColor = fPainter->HighColor();
702 		drawing_mode previousMode = fPainter->DrawingMode();
703 
704 		fPainter->SetHighColor(color);
705 		fPainter->SetDrawingMode(B_OP_OVER);
706 		fPainter->StrokeLine(start, end);
707 
708 		fPainter->SetDrawingMode(previousMode);
709 		fPainter->SetHighColor(previousColor);
710 	}
711 
712 	_CopyToFront(touched);
713 }
714 
715 // this function is used to draw a one pixel wide rect
716 void
717 DrawingEngine::StrokeRect(BRect r, const rgb_color &color)
718 {
719 	CRASH_IF_NOT_LOCKED
720 
721 	make_rect_valid(r);
722 	BRect clipped = fPainter->ClipRect(r);
723 	if (clipped.IsValid()) {
724 		AutoFloatingOverlaysHider _(fGraphicsCard, clipped);
725 
726 		fPainter->StrokeRect(r, color);
727 
728 		_CopyToFront(clipped);
729 	}
730 }
731 
732 
733 void
734 DrawingEngine::FillRect(BRect r, const rgb_color& color)
735 {
736 	CRASH_IF_NOT_LOCKED
737 
738 	// NOTE: Write locking because we might use HW acceleration.
739 	// This needs to be investigated, I'm doing this because of
740 	// gut feeling.
741 	make_rect_valid(r);
742 	r = fPainter->ClipRect(r);
743 	if (!r.IsValid())
744 		return;
745 
746 	AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, r);
747 
748 	// try hardware optimized version first
749 	if (fAvailableHWAccleration & HW_ACC_FILL_REGION) {
750 		BRegion region(r);
751 		region.IntersectWith(fPainter->ClippingRegion());
752 		fGraphicsCard->FillRegion(region, color,
753 			fSuspendSyncLevel == 0 || overlaysHider.WasHidden());
754 
755 		if (fGraphicsCard->IsDoubleBuffered())
756 			_CopyToFront(r);
757 	} else {
758 		fPainter->FillRect(r, color);
759 		_CopyToFront(r);
760 	}
761 }
762 
763 
764 void
765 DrawingEngine::FillRegion(BRegion& r, const rgb_color& color)
766 {
767 	CRASH_IF_NOT_LOCKED
768 
769 	// NOTE: region expected to be already clipped correctly!!
770 	BRect frame = r.Frame();
771 	if (!fPainter->Bounds().Contains(frame)) {
772 		// NOTE: I am not quite sure yet how this can happen, but appearantly it can (see bug 634)
773 		// This function is used for internal app_server painting, in the case of bug 634,
774 		// the background of views is painted. But the view region should never be outside the
775 		// frame buffer bounds.
776 //		char message[1024];
777 //		BRect bounds = fPainter->Bounds();
778 //		sprintf(message, "FillRegion() - painter: (%d, %d)->(%d, %d), region: (%d, %d)->(%d, %d)",
779 //			(int)bounds.left, (int)bounds.top, (int)bounds.right, (int)bounds.bottom,
780 //			(int)frame.left, (int)frame.top, (int)frame.right, (int)frame.bottom);
781 //		debugger(message);
782 		return;
783 	}
784 
785 	AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, frame);
786 
787 	// try hardware optimized version first
788 	if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0
789 		&& frame.Width() * frame.Height() > 100) {
790 		fGraphicsCard->FillRegion(r, color, fSuspendSyncLevel == 0
791 											|| overlaysHider.WasHidden());
792 		if (fGraphicsCard->IsDoubleBuffered())
793 			_CopyToFront(frame);
794 	} else {
795 		int32 count = r.CountRects();
796 		for (int32 i = 0; i < count; i++)
797 			fPainter->FillRectNoClipping(r.RectAtInt(i), color);
798 
799 		_CopyToFront(frame);
800 	}
801 }
802 
803 // #pragma mark - DrawState
804 
805 void
806 DrawingEngine::StrokeRect(BRect r)
807 {
808 	CRASH_IF_NOT_LOCKED
809 
810 	// support invalid rects
811 	make_rect_valid(r);
812 	BRect clipped(r);
813 	extend_by_stroke_width(clipped, fPainter->PenSize());
814 	clipped = fPainter->ClipRect(clipped);
815 	if (clipped.IsValid()) {
816 		AutoFloatingOverlaysHider _(fGraphicsCard, clipped);
817 
818 		fPainter->StrokeRect(r);
819 
820 		_CopyToFront(clipped);
821 	}
822 }
823 
824 
825 void
826 DrawingEngine::FillRect(BRect r)
827 {
828 	CRASH_IF_NOT_LOCKED
829 
830 	make_rect_valid(r);
831 	r = fPainter->AlignAndClipRect(r);
832 	if (!r.IsValid())
833 		return;
834 
835 	AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, r);
836 
837 	bool doInSoftware = true;
838 	if ((r.Width() + 1) * (r.Height() + 1) > 100.0) {
839 		// try hardware optimized version first
840 		// if the rect is large enough
841 		if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) {
842 			if (fPainter->Pattern() == B_SOLID_HIGH
843 				&& (fPainter->DrawingMode() == B_OP_COPY
844 					|| fPainter->DrawingMode() == B_OP_OVER)) {
845 				BRegion region(r);
846 				region.IntersectWith(fPainter->ClippingRegion());
847 				fGraphicsCard->FillRegion(region, fPainter->HighColor(),
848 										  fSuspendSyncLevel == 0
849 										  || overlaysHider.WasHidden());
850 				doInSoftware = false;
851 			} else if (fPainter->Pattern() == B_SOLID_LOW
852 					&& fPainter->DrawingMode() == B_OP_COPY) {
853 				BRegion region(r);
854 				region.IntersectWith(fPainter->ClippingRegion());
855 				fGraphicsCard->FillRegion(region, fPainter->LowColor(),
856 										  fSuspendSyncLevel == 0
857 										  || overlaysHider.WasHidden());
858 				doInSoftware = false;
859 			}
860 		}
861 	}
862 
863 	if (doInSoftware && fAvailableHWAccleration & HW_ACC_INVERT_REGION
864 			&& fPainter->Pattern() == B_SOLID_HIGH
865 			&& fPainter->DrawingMode() == B_OP_INVERT) {
866 		BRegion region(r);
867 		region.IntersectWith(fPainter->ClippingRegion());
868 		fGraphicsCard->InvertRegion(region);
869 		doInSoftware = false;
870 	}
871 
872 	if (doInSoftware)
873 		fPainter->FillRect(r);
874 
875 	if (fGraphicsCard->IsDoubleBuffered())
876 		_CopyToFront(r);
877 }
878 
879 
880 void
881 DrawingEngine::FillRegion(BRegion& r)
882 {
883 	CRASH_IF_NOT_LOCKED
884 
885 	BRect clipped = fPainter->ClipRect(r.Frame());
886 	if (!clipped.IsValid())
887 		return;
888 
889 	AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, clipped);
890 
891 	bool doInSoftware = true;
892 	// try hardware optimized version first
893 	if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) {
894 		if (fPainter->Pattern() == B_SOLID_HIGH
895 			&& (fPainter->DrawingMode() == B_OP_COPY
896 				|| fPainter->DrawingMode() == B_OP_OVER)) {
897 			r.IntersectWith(fPainter->ClippingRegion());
898 			fGraphicsCard->FillRegion(r, fPainter->HighColor(),
899 									  fSuspendSyncLevel == 0
900 									  || overlaysHider.WasHidden());
901 			doInSoftware = false;
902 		} else if (fPainter->Pattern() == B_SOLID_LOW
903 				&& fPainter->DrawingMode() == B_OP_COPY) {
904 			r.IntersectWith(fPainter->ClippingRegion());
905 			fGraphicsCard->FillRegion(r, fPainter->LowColor(),
906 									  fSuspendSyncLevel == 0
907 									  || overlaysHider.WasHidden());
908 			doInSoftware = false;
909 		}
910 	}
911 
912 	if (doInSoftware && fAvailableHWAccleration & HW_ACC_INVERT_REGION
913 			&& fPainter->Pattern() == B_SOLID_HIGH
914 			&& fPainter->DrawingMode() == B_OP_INVERT) {
915 		r.IntersectWith(fPainter->ClippingRegion());
916 		fGraphicsCard->InvertRegion(r);
917 		doInSoftware = false;
918 	}
919 
920 	if (doInSoftware) {
921 
922 		BRect touched = fPainter->FillRect(r.RectAt(0));
923 
924 		int32 count = r.CountRects();
925 		for (int32 i = 1; i < count; i++)
926 			touched = touched | fPainter->FillRect(r.RectAt(i));
927 	}
928 
929 	if (fGraphicsCard->IsDoubleBuffered())
930 		_CopyToFront(r.Frame());
931 }
932 
933 
934 void
935 DrawingEngine::DrawRoundRect(BRect r, float xrad, float yrad, bool filled)
936 {
937 	CRASH_IF_NOT_LOCKED
938 
939 	// NOTE: the stroke does not extend past "r" in R5,
940 	// though I consider this unexpected behaviour.
941 	make_rect_valid(r);
942 	BRect clipped = fPainter->ClipRect(r);
943 
944 	clipped.left = floorf(clipped.left);
945 	clipped.top = floorf(clipped.top);
946 	clipped.right = ceilf(clipped.right);
947 	clipped.bottom = ceilf(clipped.bottom);
948 
949 	if (clipped.IsValid()) {
950 		AutoFloatingOverlaysHider _(fGraphicsCard, clipped);
951 
952 		BRect touched = filled ? fPainter->FillRoundRect(r, xrad, yrad)
953 							   : fPainter->StrokeRoundRect(r, xrad, yrad);
954 
955 		_CopyToFront(touched);
956 	}
957 }
958 
959 
960 void
961 DrawingEngine::DrawShape(const BRect& bounds, int32 opCount,
962 	const uint32* opList, int32 ptCount, const BPoint* ptList, bool filled)
963 {
964 	CRASH_IF_NOT_LOCKED
965 
966 	// NOTE: hides cursor regardless of if and where
967 	// shape is drawn on screen, TODO: optimize
968 	AutoFloatingOverlaysHider _(fGraphicsCard);
969 
970 	BRect touched = fPainter->DrawShape(opCount, opList,
971 										ptCount, ptList,
972 										filled);
973 
974 	_CopyToFront(touched);
975 }
976 
977 
978 void
979 DrawingEngine::DrawTriangle(BPoint* pts, const BRect& bounds, bool filled)
980 {
981 	CRASH_IF_NOT_LOCKED
982 
983 	BRect clipped(bounds);
984 	if (!filled)
985 		extend_by_stroke_width(clipped, fPainter->PenSize());
986 	clipped = fPainter->ClipRect(clipped);
987 	if (clipped.IsValid()) {
988 		AutoFloatingOverlaysHider _(fGraphicsCard, clipped);
989 
990 		if (filled)
991 			fPainter->FillTriangle(pts[0], pts[1], pts[2]);
992 		else
993 			fPainter->StrokeTriangle(pts[0], pts[1], pts[2]);
994 
995 		_CopyToFront(clipped);
996 	}
997 }
998 
999 // StrokeLine
1000 void
1001 DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end)
1002 {
1003 	CRASH_IF_NOT_LOCKED
1004 
1005 	BRect touched(start, end);
1006 	make_rect_valid(touched);
1007 	extend_by_stroke_width(touched, fPainter->PenSize());
1008 	touched = fPainter->ClipRect(touched);
1009 	if (touched.IsValid()) {
1010 		AutoFloatingOverlaysHider _(fGraphicsCard, touched);
1011 
1012 		fPainter->StrokeLine(start, end);
1013 
1014 		_CopyToFront(touched);
1015 	}
1016 }
1017 
1018 // StrokeLineArray
1019 void
1020 DrawingEngine::StrokeLineArray(int32 numLines,
1021 	const LineArrayData *linedata)
1022 {
1023 	CRASH_IF_NOT_LOCKED
1024 
1025 	if (!linedata || numLines <= 0)
1026 		return;
1027 
1028 	// figure out bounding box for line array
1029 	const LineArrayData *data = (const LineArrayData *)&(linedata[0]);
1030 	BRect touched(min_c(data->pt1.x, data->pt2.x),
1031 				  min_c(data->pt1.y, data->pt2.y),
1032 				  max_c(data->pt1.x, data->pt2.x),
1033 				  max_c(data->pt1.y, data->pt2.y));
1034 
1035 	for (int32 i = 1; i < numLines; i++) {
1036 		data = (const LineArrayData *)&(linedata[i]);
1037 		BRect box(min_c(data->pt1.x, data->pt2.x),
1038 				  min_c(data->pt1.y, data->pt2.y),
1039 				  max_c(data->pt1.x, data->pt2.x),
1040 				  max_c(data->pt1.y, data->pt2.y));
1041 		touched = touched | box;
1042 	}
1043 	extend_by_stroke_width(touched, fPainter->PenSize());
1044 	touched = fPainter->ClipRect(touched);
1045 	if (touched.IsValid()) {
1046 		AutoFloatingOverlaysHider _(fGraphicsCard, touched);
1047 
1048 		data = (const LineArrayData *)&(linedata[0]);
1049 
1050 		// store current graphics state, we mess with the
1051 		// high color and pattern...
1052 		rgb_color oldColor = fPainter->HighColor();
1053 		struct pattern pattern = fPainter->Pattern();
1054 
1055 		fPainter->SetHighColor(data->color);
1056 		fPainter->SetPattern(B_SOLID_HIGH);
1057 		fPainter->StrokeLine(data->pt1, data->pt2);
1058 
1059 		for (int32 i = 1; i < numLines; i++) {
1060 			data = (const LineArrayData *)&(linedata[i]);
1061 			fPainter->SetHighColor(data->color);
1062 			fPainter->StrokeLine(data->pt1, data->pt2);
1063 		}
1064 
1065 		// restore correct drawing state highcolor and pattern
1066 		fPainter->SetHighColor(oldColor);
1067 		fPainter->SetPattern(pattern);
1068 
1069 		_CopyToFront(touched);
1070 	}
1071 }
1072 
1073 // #pragma mark -
1074 
1075 BPoint
1076 DrawingEngine::DrawString(const char* string, int32 length,
1077 						  const BPoint& pt, escapement_delta* delta)
1078 {
1079 	CRASH_IF_NOT_LOCKED
1080 
1081 	BPoint penLocation = pt;
1082 
1083 	// try a fast clipping path
1084 	if (fPainter->ClippingRegion()) {
1085 		float fontSize = fPainter->Font().Size();
1086 		BRect clippingFrame = fPainter->ClippingRegion()->Frame();
1087 		if (pt.x > clippingFrame.right || pt.y + fontSize < clippingFrame.top
1088 			|| pt.y - fontSize > clippingFrame.bottom) {
1089 			penLocation.x += StringWidth(string, length, delta);
1090 			return penLocation;
1091 		}
1092 	}
1093 
1094 	// use a FontCacheRefernece to speed up the second pass of
1095 	// drawing the string
1096 	FontCacheReference cacheReference;
1097 
1098 //bigtime_t now = system_time();
1099 // TODO: BoundingBox is quite slow!! Optimizing it will be beneficial.
1100 // Cursiously, the DrawString after it is actually faster!?!
1101 // TODO: make the availability of the hardware cursor part of the
1102 // HW acceleration flags and skip all calculations for HideFloatingOverlays
1103 // in case we don't have one.
1104 // TODO: Watch out about penLocation and use Painter::PenLocation() when
1105 // not using BoundindBox anymore.
1106 	BRect b = fPainter->BoundingBox(string, length, pt, &penLocation, delta,
1107 		&cacheReference);
1108 	// stop here if we're supposed to render outside of the clipping
1109 	b = fPainter->ClipRect(b);
1110 	if (b.IsValid()) {
1111 //printf("bounding box '%s': %lld µs\n", string, system_time() - now);
1112 		AutoFloatingOverlaysHider _(fGraphicsCard, b);
1113 
1114 //now = system_time();
1115 		BRect touched = fPainter->DrawString(string, length, pt, delta,
1116 			&cacheReference);
1117 //printf("drawing string: %lld µs\n", system_time() - now);
1118 
1119 		_CopyToFront(touched);
1120 	}
1121 
1122 	return penLocation;
1123 }
1124 
1125 // StringWidth
1126 float
1127 DrawingEngine::StringWidth(const char* string, int32 length,
1128 						   escapement_delta* delta)
1129 {
1130 	return fPainter->StringWidth(string, length, delta);
1131 }
1132 
1133 // StringWidth
1134 float
1135 DrawingEngine::StringWidth(const char* string, int32 length,
1136 	const ServerFont& font, escapement_delta* delta)
1137 {
1138 	return font.StringWidth(string, length, delta);
1139 }
1140 
1141 // #pragma mark -
1142 
1143 // DumpToFile
1144 bool
1145 DrawingEngine::DumpToFile(const char *path)
1146 {
1147 	CRASH_IF_NOT_EXCLUSIVE_LOCKED
1148 
1149 	RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer();
1150 	if (buffer) {
1151 		BRect bounds(0.0, 0.0, buffer->Width() - 1, buffer->Height() - 1);
1152 		SaveToPNG(path, bounds, buffer->ColorSpace(),
1153 				  buffer->Bits(),
1154 				  buffer->BitsLength(),
1155 				  buffer->BytesPerRow());
1156 		return true;
1157 	}
1158 	return false;
1159 }
1160 
1161 // DumpToBitmap
1162 ServerBitmap*
1163 DrawingEngine::DumpToBitmap()
1164 {
1165 	return NULL;
1166 }
1167 
1168 status_t
1169 DrawingEngine::ReadBitmap(ServerBitmap *bitmap, bool drawCursor, BRect bounds)
1170 {
1171 	CRASH_IF_NOT_EXCLUSIVE_LOCKED
1172 
1173 	RenderingBuffer *buffer = fGraphicsCard->FrontBuffer();
1174 	if (!buffer)
1175 		return B_ERROR;
1176 
1177 	BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1);
1178 	bounds = bounds & clip;
1179 	AutoFloatingOverlaysHider _(fGraphicsCard, bounds);
1180 
1181 	status_t result = bitmap->ImportBits(buffer->Bits(), buffer->BitsLength(),
1182 		buffer->BytesPerRow(), buffer->ColorSpace(),
1183 		bounds.LeftTop(), BPoint(0, 0),
1184 		bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1);
1185 
1186 	if (drawCursor) {
1187 		ServerCursorReference cursorRef = fGraphicsCard->Cursor();
1188 		ServerCursor* cursor = cursorRef.Cursor();
1189 		if (!cursor)
1190 			return result;
1191 		int32 cursorWidth = cursor->Width();
1192 		int32 cursorHeight = cursor->Height();
1193 
1194 		BPoint cursorPosition = fGraphicsCard->CursorPosition();
1195 		cursorPosition -= bounds.LeftTop() + cursor->GetHotSpot();
1196 
1197 		BBitmap cursorArea(BRect(0, 0, cursorWidth - 1, cursorHeight - 1),
1198 			B_BITMAP_NO_SERVER_LINK, B_RGBA32);
1199 
1200 		cursorArea.ImportBits(bitmap->Bits(), bitmap->BitsLength(),
1201 			bitmap->BytesPerRow(), bitmap->ColorSpace(),
1202 			cursorPosition,	BPoint(0, 0),
1203 			cursorWidth, cursorHeight);
1204 
1205 		uint8 *bits = (uint8 *)cursorArea.Bits();
1206 		uint8 *cursorBits = (uint8 *)cursor->Bits();
1207 		for (int32 i = 0; i < cursorHeight; i++) {
1208 			for (int32 j = 0; j < cursorWidth; j++) {
1209 				uint8 alpha = 255 - cursorBits[3];
1210 				bits[0] = ((bits[0] * alpha) >> 8) + cursorBits[0];
1211 				bits[1] = ((bits[1] * alpha) >> 8) + cursorBits[1];
1212 				bits[2] = ((bits[2] * alpha) >> 8) + cursorBits[2];
1213 				cursorBits += 4;
1214 				bits += 4;
1215 			}
1216 		}
1217 
1218 		bitmap->ImportBits(cursorArea.Bits(), cursorArea.BitsLength(),
1219 			cursorArea.BytesPerRow(), cursorArea.ColorSpace(),
1220 			BPoint(0, 0), cursorPosition,
1221 			cursorWidth, cursorHeight);
1222 	}
1223 
1224 	return result;
1225 }
1226 
1227 // #pragma mark -
1228 
1229 BRect
1230 DrawingEngine::_CopyRect(BRect src, int32 xOffset, int32 yOffset) const
1231 {
1232 	// TODO: assumes drawing buffer is 32 bits (which it currently always is)
1233 	BRect dst;
1234 	RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer();
1235 	if (buffer) {
1236 		BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1);
1237 
1238 		dst = src;
1239 		dst.OffsetBy(xOffset, yOffset);
1240 
1241 		if (clip.Intersects(src) && clip.Intersects(dst)) {
1242 			uint32 bytesPerRow = buffer->BytesPerRow();
1243 			uint8* bits = (uint8*)buffer->Bits();
1244 
1245 			// clip source rect
1246 			src = src & clip;
1247 			// clip dest rect
1248 			dst = dst & clip;
1249 			// move dest back over source and clip source to dest
1250 			dst.OffsetBy(-xOffset, -yOffset);
1251 			src = src & dst;
1252 
1253 			// calc offset in buffer
1254 			bits += (int32)src.left * 4 + (int32)src.top * bytesPerRow;
1255 
1256 			uint32 width = src.IntegerWidth() + 1;
1257 			uint32 height = src.IntegerHeight() + 1;
1258 
1259 			_CopyRect(bits, width, height, bytesPerRow, xOffset, yOffset);
1260 
1261 			// offset dest again, because it is return value
1262 			dst.OffsetBy(xOffset, yOffset);
1263 		}
1264 	}
1265 	return dst;
1266 }
1267 
1268 
1269 void
1270 DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height,
1271 	uint32 bytesPerRow, int32 xOffset, int32 yOffset) const
1272 {
1273 	// TODO: assumes drawing buffer is 32 bits (which it currently always is)
1274 	int32 xIncrement;
1275 	int32 yIncrement;
1276 
1277 	if (yOffset == 0 && xOffset > 0) {
1278 		// copy from right to left
1279 		xIncrement = -1;
1280 		src += (width - 1) * 4;
1281 	} else {
1282 		// copy from left to right
1283 		xIncrement = 1;
1284 	}
1285 
1286 	if (yOffset > 0) {
1287 		// copy from bottom to top
1288 		yIncrement = -bytesPerRow;
1289 		src += (height - 1) * bytesPerRow;
1290 	} else {
1291 		// copy from top to bottom
1292 		yIncrement = bytesPerRow;
1293 	}
1294 
1295 	uint8* dst = src + yOffset * bytesPerRow + xOffset * 4;
1296 
1297 	if (xIncrement == 1) {
1298 		uint8 tmpBuffer[width * 4];
1299 		for (uint32 y = 0; y < height; y++) {
1300 			// NOTE: read into temporary scanline buffer,
1301 			// avoid memcpy because it might be graphics card memory
1302 			gfxcpy32(tmpBuffer, src, width * 4);
1303 			// write back temporary scanline buffer
1304 			// NOTE: **don't read and write over the PCI bus
1305 			// at the same time**
1306 			memcpy(dst, tmpBuffer, width * 4);
1307 // NOTE: this (instead of the two pass copy above) might
1308 // speed up QEMU -> ?!? (would depend on how it emulates
1309 // the PCI bus...)
1310 // TODO: would be nice if we actually knew
1311 // if we're operating in graphics memory or main memory...
1312 //memcpy(dst, src, width * 4);
1313 			src += yIncrement;
1314 			dst += yIncrement;
1315 		}
1316 	} else {
1317 		for (uint32 y = 0; y < height; y++) {
1318 			uint32* srcHandle = (uint32*)src;
1319 			uint32* dstHandle = (uint32*)dst;
1320 			for (uint32 x = 0; x < width; x++) {
1321 				*dstHandle = *srcHandle;
1322 				srcHandle += xIncrement;
1323 				dstHandle += xIncrement;
1324 			}
1325 			src += yIncrement;
1326 			dst += yIncrement;
1327 		}
1328 	}
1329 }
1330 
1331 
1332 inline void
1333 DrawingEngine::_CopyToFront(const BRect& frame)
1334 {
1335 	if (fCopyToFront)
1336 		fGraphicsCard->Invalidate(frame);
1337 }
1338 
1339 
1340