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