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