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