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
42static inline void
43make_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
58static inline void
59extend_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
67class 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
100class DrawTransaction {
101public:
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
169private:
170	DrawingEngine *fEngine;
171	bool fOverlaysHidden;
172	BRegion fDirty;
173};
174
175
176//	#pragma mark -
177
178
179DrawingEngine::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
191DrawingEngine::~DrawingEngine()
192{
193	SetHWInterface(NULL);
194}
195
196
197// #pragma mark - locking
198
199
200bool
201DrawingEngine::LockParallelAccess()
202{
203	return fGraphicsCard->LockParallelAccess();
204}
205
206
207#if DEBUG
208bool
209DrawingEngine::IsParallelAccessLocked() const
210{
211	return fGraphicsCard->IsParallelAccessLocked();
212}
213#endif
214
215
216void
217DrawingEngine::UnlockParallelAccess()
218{
219	fGraphicsCard->UnlockParallelAccess();
220}
221
222
223bool
224DrawingEngine::LockExclusiveAccess()
225{
226	return fGraphicsCard->LockExclusiveAccess();
227}
228
229
230bool
231DrawingEngine::IsExclusiveAccessLocked() const
232{
233	return fGraphicsCard->IsExclusiveAccessLocked();
234}
235
236
237void
238DrawingEngine::UnlockExclusiveAccess()
239{
240	fGraphicsCard->UnlockExclusiveAccess();
241}
242
243
244// #pragma mark -
245
246
247void
248DrawingEngine::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
267void
268DrawingEngine::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
285void
286DrawingEngine::SetCopyToFrontEnabled(bool enable)
287{
288	fCopyToFront = enable;
289}
290
291
292void
293DrawingEngine::CopyToFront(/*const*/ BRegion& region)
294{
295	fGraphicsCard->InvalidateRegion(region);
296}
297
298
299// #pragma mark -
300
301
302//! the DrawingEngine needs to be locked!
303void
304DrawingEngine::ConstrainClippingRegion(const BRegion* region)
305{
306	ASSERT_PARALLEL_LOCKED();
307
308	fPainter->ConstrainClipping(region);
309}
310
311
312void
313DrawingEngine::SetDrawState(const DrawState* state, int32 xOffset,
314	int32 yOffset)
315{
316	fPainter->SetDrawState(state, xOffset, yOffset);
317}
318
319
320void
321DrawingEngine::SetHighColor(const rgb_color& color)
322{
323	fPainter->SetHighColor(color);
324}
325
326
327void
328DrawingEngine::SetLowColor(const rgb_color& color)
329{
330	fPainter->SetLowColor(color);
331}
332
333
334void
335DrawingEngine::SetPenSize(float size)
336{
337	fPainter->SetPenSize(size);
338}
339
340
341void
342DrawingEngine::SetStrokeMode(cap_mode lineCap, join_mode joinMode,
343	float miterLimit)
344{
345	fPainter->SetStrokeMode(lineCap, joinMode, miterLimit);
346}
347
348
349void
350DrawingEngine::SetFillRule(int32 fillRule)
351{
352	fPainter->SetFillRule(fillRule);
353}
354
355
356void
357DrawingEngine::SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc)
358{
359	fPainter->SetBlendingMode(srcAlpha, alphaFunc);
360}
361
362
363void
364DrawingEngine::SetPattern(const struct pattern& pattern)
365{
366	fPainter->SetPattern(pattern);
367}
368
369
370void
371DrawingEngine::SetDrawingMode(drawing_mode mode)
372{
373	fPainter->SetDrawingMode(mode);
374}
375
376
377void
378DrawingEngine::SetDrawingMode(drawing_mode mode, drawing_mode& oldMode)
379{
380	oldMode = fPainter->DrawingMode();
381	fPainter->SetDrawingMode(mode);
382}
383
384
385void
386DrawingEngine::SetFont(const ServerFont& font)
387{
388	fPainter->SetFont(font);
389}
390
391
392void
393DrawingEngine::SetFont(const DrawState* state)
394{
395	fPainter->SetFont(state);
396}
397
398
399void
400DrawingEngine::SetTransform(const BAffineTransform& transform, int32 xOffset,
401	int32 yOffset)
402{
403	fPainter->SetTransform(transform, xOffset, yOffset);
404}
405
406
407// #pragma mark -
408
409
410void
411DrawingEngine::SuspendAutoSync()
412{
413	ASSERT_PARALLEL_LOCKED();
414
415	fSuspendSyncLevel++;
416}
417
418
419void
420DrawingEngine::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
487struct 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
536static bool
537is_left_of(const BRect& a, const BRect& b)
538{
539	return (a.right < b.left);
540}
541
542
543static bool
544is_above(const BRect& a, const BRect& b)
545{
546	return (a.bottom < b.top);
547}
548
549
550void
551DrawingEngine::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
676void
677DrawingEngine::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
698void
699DrawingEngine::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
710void
711DrawingEngine::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
738void
739DrawingEngine::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
759void
760DrawingEngine::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
771void
772DrawingEngine::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
783void
784DrawingEngine::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
808void
809DrawingEngine::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
830void
831DrawingEngine::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
847void
848DrawingEngine::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
865void
866DrawingEngine::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*/
875void
876DrawingEngine::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
901void
902DrawingEngine::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
915void
916DrawingEngine::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
941void
942DrawingEngine::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
981void
982DrawingEngine::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
998void
999DrawingEngine::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
1054void
1055DrawingEngine::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
1070void
1071DrawingEngine::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
1114void
1115DrawingEngine::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
1130void
1131DrawingEngine::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
1156void
1157DrawingEngine::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
1178void
1179DrawingEngine::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
1206void
1207DrawingEngine::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
1232void
1233DrawingEngine::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
1251void
1252DrawingEngine::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
1265void
1266DrawingEngine::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
1281void
1282DrawingEngine::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
1336BPoint
1337DrawingEngine::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
1386BPoint
1387DrawingEngine::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
1413float
1414DrawingEngine::StringWidth(const char* string, int32 length,
1415	escapement_delta* delta)
1416{
1417	return fPainter->StringWidth(string, length, delta);
1418}
1419
1420
1421float
1422DrawingEngine::StringWidth(const char* string, int32 length,
1423	const ServerFont& font, escapement_delta* delta)
1424{
1425	return font.StringWidth(string, length, delta);
1426}
1427
1428
1429BPoint
1430DrawingEngine::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
1450BPoint
1451DrawingEngine::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
1466ServerBitmap*
1467DrawingEngine::DumpToBitmap()
1468{
1469	return NULL;
1470}
1471
1472
1473status_t
1474DrawingEngine::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			cursorArea.Bounds().Size());
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
1536BRect
1537DrawingEngine::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
1577void
1578DrawingEngine::SetRendererOffset(int32 offsetX, int32 offsetY)
1579{
1580	fPainter->SetRendererOffset(offsetX, offsetY);
1581}
1582
1583
1584void
1585DrawingEngine::_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