xref: /haiku/src/servers/app/drawing/DrawingEngine.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
1 /*
2  * Copyright 2001-2005, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  */
8 
9 #include "DrawingEngine.h"
10 
11 #include <Bitmap.h>
12 #include <stdio.h>
13 #include <algo.h>
14 #include <stack.h>
15 
16 #include "HWInterface.h"
17 #include "DrawState.h"
18 #include "Painter.h"
19 #include "PNGDump.h"
20 #include "ServerBitmap.h"
21 #include "ServerCursor.h"
22 #include "RenderingBuffer.h"
23 
24 #include "drawing_support.h"
25 
26 // make_rect_valid
27 static inline void
28 make_rect_valid(BRect& rect)
29 {
30 	if (rect.left > rect.right) {
31 		float temp = rect.left;
32 		rect.left = rect.right;
33 		rect.right = temp;
34 	}
35 	if (rect.top > rect.bottom) {
36 		float temp = rect.top;
37 		rect.top = rect.bottom;
38 		rect.bottom = temp;
39 	}
40 }
41 
42 // extend_by_stroke_width
43 static inline void
44 extend_by_stroke_width(BRect& rect, const DrawState* context)
45 {
46 	// "- 1.0" because if stroke width == 1, we don't need to extend
47 	float inset = -ceilf(context->PenSize() / 2.0 - 1.0);
48 	rect.InsetBy(inset, inset);
49 }
50 
51 
52 class FontLocker {
53 	public:
54 		FontLocker(const DrawState* context)
55 			:
56 			fFont(&context->Font())
57 		{
58 			fFont->Lock();
59 		}
60 
61 		FontLocker(const ServerFont* font)
62 			:
63 			fFont(font)
64 		{
65 			fFont->Lock();
66 		}
67 
68 		~FontLocker()
69 		{
70 			fFont->Unlock();
71 		}
72 
73 	private:
74 		const ServerFont*	fFont;
75 };
76 
77 
78 //	#pragma mark -
79 
80 
81 // constructor
82 DrawingEngine::DrawingEngine(HWInterface* interface)
83 	: fPainter(new Painter()),
84 	  fGraphicsCard(interface),
85 	  fAvailableHWAccleration(0),
86 	  fSuspendSyncLevel(0)
87 {
88 }
89 
90 // destructor
91 DrawingEngine::~DrawingEngine()
92 {
93 	delete fPainter;
94 }
95 
96 // Initialize
97 status_t
98 DrawingEngine::Initialize()
99 {
100 	status_t err = B_ERROR;
101 	if (WriteLock()) {
102 		err = fGraphicsCard->Initialize();
103 		if (err < B_OK)
104 			fprintf(stderr, "HWInterface::Initialize() failed: %s\n", strerror(err));
105 		WriteUnlock();
106 	}
107 	return err;
108 }
109 
110 // Shutdown
111 void
112 DrawingEngine::Shutdown()
113 {
114 }
115 
116 // Update
117 void
118 DrawingEngine::Update()
119 {
120 	if (Lock()) {
121 		fPainter->AttachToBuffer(fGraphicsCard->DrawingBuffer());
122 		// available HW acceleration might have changed
123 		fAvailableHWAccleration = fGraphicsCard->AvailableHWAcceleration();
124 		Unlock();
125 	}
126 }
127 
128 
129 void
130 DrawingEngine::SetHWInterface(HWInterface* interface)
131 {
132 	fGraphicsCard = interface;
133 }
134 
135 // #pragma mark -
136 
137 //! the DrawingEngine needs to be locked!
138 void
139 DrawingEngine::ConstrainClippingRegion(const BRegion* region)
140 {
141 	fPainter->ConstrainClipping(region);
142 }
143 
144 // SuspendAutoSync
145 void
146 DrawingEngine::SuspendAutoSync()
147 {
148 	fSuspendSyncLevel++;
149 }
150 
151 // Sync
152 void
153 DrawingEngine::Sync()
154 {
155 	fSuspendSyncLevel--;
156 	if (fSuspendSyncLevel == 0)
157 		fGraphicsCard->Sync();
158 }
159 
160 // #pragma mark -
161 
162 // CopyRegion() does a topological sort of the rects in the
163 // region. The algorithm was suggested by Ingo Weinhold.
164 // It compares each rect with each rect and builds a tree
165 // of successors so we know the order in which they can be copied.
166 // For example, let's suppose these rects are in a BRegion:
167 //                        ************
168 //                        *    B     *
169 //                        ************
170 //      *************
171 //      *           *
172 //      *     A     ****************
173 //      *           **             *
174 //      **************             *
175 //                   *     C       *
176 //                   *             *
177 //                   *             *
178 //                   ***************
179 // When copying stuff from LEFT TO RIGHT, TOP TO BOTTOM, the
180 // result of the sort will be C, A, B. For this direction, we search
181 // for the rects that have no neighbors to their right and to their
182 // bottom, These can be copied without drawing into the area of
183 // rects yet to be copied. If you move from RIGHT TO LEFT, BOTTOM TO TOP,
184 // you go look for the ones that have no neighbors to their top and left.
185 //
186 // Here I draw some rays to illustrate LEFT TO RIGHT, TOP TO BOTTOM:
187 //                        ************
188 //                        *    B     *
189 //                        ************
190 //      *************
191 //      *           *
192 //      *     A     ****************-----------------
193 //      *           **             *
194 //      **************             *
195 //                   *     C       *
196 //                   *             *
197 //                   *             *
198 //                   ***************
199 //                   |
200 //                   |
201 //                   |
202 //                   |
203 // There are no rects in the area defined by the rays to the right
204 // and bottom of rect C, so that's the one we want to copy first
205 // (for positive x and y offsets).
206 // Since A is to the left of C and B is to the top of C, The "node"
207 // for C will point to the nodes of A and B as its "successors". Therefor,
208 // A and B will have an "indegree" of 1 for C pointing to them. C will
209 // have and "indegree" of 0, because there was no rect to which C
210 // was to the left or top of. When comparing A and B, neither is left
211 // or top from the other and in the sense that the algorithm cares about.
212 
213 // NOTE: comparisson of coordinates assumes that rects don't overlap
214 // and don't share the actual edge either (as is the case in BRegions).
215 
216 struct node {
217 			node()
218 			{
219 				pointers = NULL;
220 			}
221 			node(const BRect& r, int32 maxPointers)
222 			{
223 				init(r, maxPointers);
224 			}
225 			~node()
226 			{
227 				delete [] pointers;
228 			}
229 
230 	void	init(const BRect& r, int32 maxPointers)
231 			{
232 				rect = r;
233 				pointers = new node*[maxPointers];
234 				in_degree = 0;
235 				next_pointer = 0;
236 			}
237 
238 	void	push(node* node)
239 			{
240 				pointers[next_pointer] = node;
241 				next_pointer++;
242 			}
243 	node*	top()
244 			{
245 				return pointers[next_pointer];
246 			}
247 	node*	pop()
248 			{
249 				node* ret = top();
250 				next_pointer--;
251 				return ret;
252 			}
253 
254 	BRect	rect;
255 	int32	in_degree;
256 	node**	pointers;
257 	int32	next_pointer;
258 };
259 
260 bool
261 is_left_of(const BRect& a, const BRect& b)
262 {
263 	return (a.right < b.left);
264 }
265 bool
266 is_above(const BRect& a, const BRect& b)
267 {
268 	return (a.bottom < b.top);
269 }
270 
271 // CopyRegion
272 void
273 DrawingEngine::CopyRegion(/*const*/ BRegion* region,
274 						  int32 xOffset, int32 yOffset)
275 {
276 	// NOTE: Write locking because we might use HW acceleration.
277 	// This needs to be investigated, I'm doing this because of
278 	// gut feeling.
279 	if (WriteLock()) {
280 		BRect frame = region->Frame();
281 		frame = frame | frame.OffsetByCopy(xOffset, yOffset);
282 		fGraphicsCard->HideSoftwareCursor(frame);
283 
284 		int32 count = region->CountRects();
285 
286 		// TODO: make this step unnecessary
287 		// (by using different stack impl inside node)
288 		node nodes[count];
289 		for (int32 i= 0; i < count; i++) {
290 			nodes[i].init(region->RectAt(i), count);
291 		}
292 
293 		for (int32 i = 0; i < count; i++) {
294 			BRect a = region->RectAt(i);
295 			for (int32 k = i + 1; k < count; k++) {
296 				BRect b = region->RectAt(k);
297 				int cmp = 0;
298 				// compare horizontally
299 				if (xOffset > 0) {
300 					if (is_left_of(a, b)) {
301 						cmp -= 1;
302 					} else if (is_left_of(b, a)) {
303 						cmp += 1;
304 					}
305 				} else if (xOffset < 0) {
306 					if (is_left_of(a, b)) {
307 						cmp += 1;
308 					} else if (is_left_of(b, a)) {
309 						cmp -= 1;
310 					}
311 				}
312 				// compare vertically
313 				if (yOffset > 0) {
314 					if (is_above(a, b)) {
315 						cmp -= 1;
316 					} else if (is_above(b, a)) {
317 						cmp += 1;
318 					}
319 				} else if (yOffset < 0) {
320 					if (is_above(a, b)) {
321 						cmp += 1;
322 					} else if (is_above(b, a)) {
323 						cmp -= 1;
324 					}
325 				}
326 				// add appropriate node as successor
327 				if (cmp > 0) {
328 					nodes[i].push(&nodes[k]);
329 					nodes[k].in_degree++;
330 				} else if (cmp < 0) {
331 					nodes[k].push(&nodes[i]);
332 					nodes[i].in_degree++;
333 				}
334 			}
335 		}
336 		// put all nodes onto a stack that have an "indegree" count of zero
337 		stack<node*> inDegreeZeroNodes;
338 		for (int32 i = 0; i < count; i++) {
339 			if (nodes[i].in_degree == 0) {
340 				inDegreeZeroNodes.push(&nodes[i]);
341 			}
342 		}
343 		// pop the rects from the stack, do the actual copy operation
344 		// and decrease the "indegree" count of the other rects not
345 		// currently on the stack and to which the current rect pointed
346 		// to. If their "indegree" count reaches zero, put them onto the
347 		// stack as well.
348 
349 		clipping_rect* sortedRectList = NULL;
350 		int32 nextSortedIndex = 0;
351 
352 		if (fAvailableHWAccleration & HW_ACC_COPY_REGION)
353 			sortedRectList = new clipping_rect[count];
354 
355 		while (!inDegreeZeroNodes.empty()) {
356 			node* n = inDegreeZeroNodes.top();
357 			inDegreeZeroNodes.pop();
358 
359 			// do the software implementation or add to sorted
360 			// rect list for using the HW accelerated version
361 			// later
362 			if (sortedRectList) {
363 				sortedRectList[nextSortedIndex].left	= (int32)n->rect.left;
364 				sortedRectList[nextSortedIndex].top		= (int32)n->rect.top;
365 				sortedRectList[nextSortedIndex].right	= (int32)n->rect.right;
366 				sortedRectList[nextSortedIndex].bottom	= (int32)n->rect.bottom;
367 				nextSortedIndex++;
368 			} else {
369 				BRect touched = _CopyRect(n->rect, xOffset, yOffset);
370 				fGraphicsCard->Invalidate(touched);
371 			}
372 
373 			for (int32 k = 0; k < n->next_pointer; k++) {
374 				n->pointers[k]->in_degree--;
375 				if (n->pointers[k]->in_degree == 0)
376 					inDegreeZeroNodes.push(n->pointers[k]);
377 			}
378 		}
379 
380 		// trigger the HW accelerated version if is was available
381 		if (sortedRectList)
382 			fGraphicsCard->CopyRegion(sortedRectList, count, xOffset, yOffset);
383 
384 		delete[] sortedRectList;
385 
386 		fGraphicsCard->ShowSoftwareCursor();
387 
388 		WriteUnlock();
389 	}
390 }
391 
392 // InvertRect
393 void
394 DrawingEngine::InvertRect(BRect r)
395 {
396 	// NOTE: Write locking because we might use HW acceleration.
397 	// This needs to be investigated, I'm doing this because of
398 	// gut feeling.
399 	if (WriteLock()) {
400 		make_rect_valid(r);
401 		r = fPainter->ClipRect(r);
402 		if (r.IsValid()) {
403 			fGraphicsCard->HideSoftwareCursor(r);
404 
405 			// try hardware optimized version first
406 			if (fAvailableHWAccleration & HW_ACC_INVERT_REGION) {
407 				BRegion region(r);
408 				region.IntersectWith(fPainter->ClippingRegion());
409 				fGraphicsCard->InvertRegion(region);
410 			} else {
411 				fPainter->InvertRect(r);
412 
413 				fGraphicsCard->Invalidate(r);
414 			}
415 
416 			fGraphicsCard->ShowSoftwareCursor();
417 		}
418 
419 		WriteUnlock();
420 	}
421 }
422 
423 // DrawBitmap
424 void
425 DrawingEngine::DrawBitmap(ServerBitmap *bitmap,
426 						  const BRect &source, const BRect &dest,
427 						  const DrawState *d)
428 {
429 	if (Lock()) {
430 		BRect clipped = fPainter->ClipRect(dest);
431 		if (clipped.IsValid()) {
432 			fGraphicsCard->HideSoftwareCursor(clipped);
433 
434 			fPainter->SetDrawState(d);
435 			fPainter->DrawBitmap(bitmap, source, dest);
436 
437 			fGraphicsCard->Invalidate(clipped);
438 			fGraphicsCard->ShowSoftwareCursor();
439 		}
440 
441 		Unlock();
442 	}
443 }
444 
445 // DrawArc
446 void
447 DrawingEngine::DrawArc(BRect r, const float &angle,
448 					   const float &span, const DrawState *d,
449 					   bool filled)
450 {
451 	if (Lock()) {
452 		make_rect_valid(r);
453 		BRect clipped(r);
454 		if (!filled)
455 			extend_by_stroke_width(clipped, d);
456 		clipped = fPainter->ClipRect(r);
457 		if (clipped.IsValid()) {
458 			fGraphicsCard->HideSoftwareCursor(clipped);
459 
460 			fPainter->SetDrawState(d);
461 
462 			float xRadius = r.Width() / 2.0;
463 			float yRadius = r.Height() / 2.0;
464 			BPoint center(r.left + xRadius,
465 						  r.top + yRadius);
466 
467 			if (filled)
468 				fPainter->FillArc(center, xRadius, yRadius, angle, span);
469 			else
470 				fPainter->StrokeArc(center, xRadius, yRadius, angle, span);
471 
472 			fGraphicsCard->Invalidate(clipped);
473 			fGraphicsCard->ShowSoftwareCursor();
474 		}
475 
476 		Unlock();
477 	}
478 }
479 
480 // DrawBezier
481 void
482 DrawingEngine::DrawBezier(BPoint *pts, const DrawState *d, bool filled)
483 {
484 	if (Lock()) {
485 		// TODO: figure out bounds and hide cursor depending on that
486 		fGraphicsCard->HideSoftwareCursor();
487 
488 		fPainter->SetDrawState(d);
489 		BRect touched = fPainter->DrawBezier(pts, filled);
490 
491 		fGraphicsCard->Invalidate(touched);
492 		fGraphicsCard->ShowSoftwareCursor();
493 
494 		Unlock();
495 	}
496 }
497 
498 // DrawEllipse
499 void
500 DrawingEngine::DrawEllipse(BRect r, const DrawState *d, bool filled)
501 {
502 	if (Lock()) {
503 		make_rect_valid(r);
504 		BRect clipped = r;
505 		if (!filled)
506 			extend_by_stroke_width(clipped, d);
507 		clipped = fPainter->ClipRect(clipped);
508 		if (clipped.IsValid()) {
509 			fGraphicsCard->HideSoftwareCursor(clipped);
510 
511 			fPainter->SetDrawState(d);
512 			fPainter->DrawEllipse(r, filled);
513 
514 			fGraphicsCard->Invalidate(clipped);
515 			fGraphicsCard->ShowSoftwareCursor();
516 		}
517 
518 		Unlock();
519 	}
520 }
521 
522 // DrawPolygon
523 void
524 DrawingEngine::DrawPolygon(BPoint* ptlist, int32 numpts,
525 						   BRect bounds, const DrawState* d,
526 						   bool filled, bool closed)
527 {
528 	if (Lock()) {
529 		make_rect_valid(bounds);
530 		if (!filled)
531 			extend_by_stroke_width(bounds, d);
532 		bounds = fPainter->ClipRect(bounds);
533 		if (bounds.IsValid()) {
534 			fGraphicsCard->HideSoftwareCursor(bounds);
535 
536 			fPainter->SetDrawState(d);
537 			fPainter->DrawPolygon(ptlist, numpts, filled, closed);
538 
539 			fGraphicsCard->Invalidate(bounds);
540 			fGraphicsCard->ShowSoftwareCursor();
541 		}
542 
543 		Unlock();
544 	}
545 }
546 
547 // #pragma mark - RGBColor
548 
549 void
550 DrawingEngine::StrokePoint(const BPoint& pt, const RGBColor &color)
551 {
552 	StrokeLine(pt, pt, color);
553 }
554 
555 // StrokeLine
556 //
557 // * this function is only used by Decorators
558 // * it assumes a one pixel wide line
559 void
560 DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end, const RGBColor &color)
561 {
562 	if (Lock()) {
563 		BRect touched(start, end);
564 		make_rect_valid(touched);
565 		touched = fPainter->ClipRect(touched);
566 		fGraphicsCard->HideSoftwareCursor(touched);
567 
568 		if (!fPainter->StraightLine(start, end, color.GetColor32())) {
569 			DrawState context;
570 			context.SetHighColor(color);
571 			context.SetDrawingMode(B_OP_OVER);
572 			StrokeLine(start, end, &context);
573 		} else {
574 			fGraphicsCard->Invalidate(touched);
575 		}
576 		fGraphicsCard->ShowSoftwareCursor();
577 		Unlock();
578 	}
579 }
580 
581 // this function is used to draw a one pixel wide rect
582 void
583 DrawingEngine::StrokeRect(BRect r, const RGBColor &color)
584 {
585 	if (Lock()) {
586 		make_rect_valid(r);
587 		BRect clipped = fPainter->ClipRect(r);
588 		if (clipped.IsValid()) {
589 			fGraphicsCard->HideSoftwareCursor(clipped);
590 
591 			fPainter->StrokeRect(r, color.GetColor32());
592 
593 			fGraphicsCard->Invalidate(clipped);
594 			fGraphicsCard->ShowSoftwareCursor();
595 		}
596 
597 		Unlock();
598 	}
599 }
600 
601 
602 void
603 DrawingEngine::FillRect(BRect r, const RGBColor& color)
604 {
605 	// NOTE: Write locking because we might use HW acceleration.
606 	// This needs to be investigated, I'm doing this because of
607 	// gut feeling.
608 	if (WriteLock()) {
609 		make_rect_valid(r);
610 		r = fPainter->ClipRect(r);
611 		if (r.IsValid()) {
612 			bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r);
613 
614 			// try hardware optimized version first
615 			if (fAvailableHWAccleration & HW_ACC_FILL_REGION) {
616 				BRegion region(r);
617 				region.IntersectWith(fPainter->ClippingRegion());
618 				fGraphicsCard->FillRegion(region, color,
619 										  fSuspendSyncLevel == 0
620 										  || cursorTouched);
621 			} else {
622 				fPainter->FillRect(r, color.GetColor32());
623 
624 				fGraphicsCard->Invalidate(r);
625 			}
626 
627 			if (cursorTouched)
628 				fGraphicsCard->ShowSoftwareCursor();
629 		}
630 
631 		WriteUnlock();
632 	}
633 }
634 
635 
636 void
637 DrawingEngine::FillRegion(BRegion& r, const RGBColor& color)
638 {
639 	// NOTE: Write locking because we might use HW acceleration.
640 	// This needs to be investigated, I'm doing this because of
641 	// gut feeling.
642 	// NOTE: region expected to be already clipped correctly!!
643 	if (WriteLock()) {
644 		BRect frame = r.Frame();
645 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(frame);
646 
647 		bool doInSoftware = true;
648 		// try hardware optimized version first
649 		if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0
650 			&& frame.Width() * frame.Height() > 100) {
651 			fGraphicsCard->FillRegion(r, color, fSuspendSyncLevel == 0
652 												|| cursorTouched);
653 			doInSoftware = false;
654 		}
655 
656 		if (doInSoftware) {
657 
658 			int32 count = r.CountRects();
659 			for (int32 i = 0; i < count; i++) {
660 				fPainter->FillRectNoClipping(r.RectAt(i), color.GetColor32());
661 			}
662 
663 			fGraphicsCard->Invalidate(r.Frame());
664 		}
665 
666 		if (cursorTouched)
667 			fGraphicsCard->ShowSoftwareCursor();
668 
669 		WriteUnlock();
670 	}
671 }
672 
673 // #pragma mark - DrawState
674 
675 void
676 DrawingEngine::StrokeRect(BRect r, const DrawState *d)
677 {
678 	// support invalid rects
679 	make_rect_valid(r);
680 	BRect clipped(r);
681 	extend_by_stroke_width(clipped, d);
682 	clipped = fPainter->ClipRect(clipped);
683 	if (clipped.IsValid()) {
684 
685 		fGraphicsCard->HideSoftwareCursor(clipped);
686 
687 		fPainter->SetDrawState(d);
688 		fPainter->StrokeRect(r);
689 
690 		fGraphicsCard->Invalidate(clipped);
691 		fGraphicsCard->ShowSoftwareCursor();
692 	}
693 }
694 
695 
696 void
697 DrawingEngine::FillRect(BRect r, const DrawState *d)
698 {
699 	// NOTE: Write locking because we might use HW acceleration.
700 	// This needs to be investigated, I'm doing this because of
701 	// gut feeling.
702 	if (WriteLock()) {
703 		make_rect_valid(r);
704 		r = fPainter->ClipRect(r);
705 		if (r.IsValid()) {
706 			bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r);
707 
708 			bool doInSoftware = true;
709 			if ((r.Width() + 1) * (r.Height() + 1) > 100.0) {
710 				// try hardware optimized version first
711 				// if the rect is large enough
712 				if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) {
713 					if (d->GetPattern() == B_SOLID_HIGH
714 						&& (d->GetDrawingMode() == B_OP_COPY
715 							|| d->GetDrawingMode() == B_OP_OVER)) {
716 						BRegion region(r);
717 						region.IntersectWith(fPainter->ClippingRegion());
718 						fGraphicsCard->FillRegion(region, d->HighColor(),
719 												  fSuspendSyncLevel == 0
720 												  || cursorTouched);
721 						doInSoftware = false;
722 					} else if (d->GetPattern() == B_SOLID_LOW
723 							   && d->GetDrawingMode() == B_OP_COPY) {
724 						BRegion region(r);
725 						region.IntersectWith(fPainter->ClippingRegion());
726 						fGraphicsCard->FillRegion(region, d->LowColor(),
727 												  fSuspendSyncLevel == 0
728 												  || cursorTouched);
729 						doInSoftware = false;
730 					}
731 				}
732 			}
733 			if (doInSoftware) {
734 				fPainter->SetDrawState(d);
735 				fPainter->FillRect(r);
736 
737 				fGraphicsCard->Invalidate(r);
738 			}
739 
740 			if (cursorTouched)
741 				fGraphicsCard->ShowSoftwareCursor();
742 		}
743 
744 		WriteUnlock();
745 	}
746 }
747 
748 
749 void
750 DrawingEngine::FillRegion(BRegion& r, const DrawState *d)
751 {
752 	// NOTE: Write locking because we might use HW acceleration.
753 	// This needs to be investigated, I'm doing this because of
754 	// gut feeling.
755 	if (WriteLock()) {
756 		BRect clipped = fPainter->ClipRect(r.Frame());
757 		if (clipped.IsValid()) {
758 			bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped);
759 
760 			bool doInSoftware = true;
761 			// try hardware optimized version first
762 			if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) {
763 				if (d->GetPattern() == B_SOLID_HIGH
764 					&& (d->GetDrawingMode() == B_OP_COPY
765 						|| d->GetDrawingMode() == B_OP_OVER)) {
766 					r.IntersectWith(fPainter->ClippingRegion());
767 					fGraphicsCard->FillRegion(r, d->HighColor(),
768 											  fSuspendSyncLevel == 0
769 											  || cursorTouched);
770 					doInSoftware = false;
771 				} else if (d->GetPattern() == B_SOLID_LOW
772 						   && d->GetDrawingMode() == B_OP_COPY) {
773 					r.IntersectWith(fPainter->ClippingRegion());
774 					fGraphicsCard->FillRegion(r, d->LowColor(),
775 											  fSuspendSyncLevel == 0
776 											  || cursorTouched);
777 					doInSoftware = false;
778 				}
779 			}
780 
781 			if (doInSoftware) {
782 				fPainter->SetDrawState(d);
783 
784 				BRect touched = fPainter->FillRect(r.RectAt(0));
785 
786 				int32 count = r.CountRects();
787 				for (int32 i = 1; i < count; i++) {
788 					touched = touched | fPainter->FillRect(r.RectAt(i));
789 				}
790 
791 				fGraphicsCard->Invalidate(touched);
792 			}
793 
794 			if (cursorTouched)
795 				fGraphicsCard->ShowSoftwareCursor();
796 		}
797 
798 		WriteUnlock();
799 	}
800 }
801 
802 
803 void
804 DrawingEngine::DrawRoundRect(BRect r, float xrad, float yrad,
805 	const DrawState* d, bool filled)
806 {
807 	// NOTE: the stroke does not extend past "r" in R5,
808 	// though I consider this unexpected behaviour.
809 	make_rect_valid(r);
810 	BRect clipped = fPainter->ClipRect(r);
811 	if (clipped.IsValid()) {
812 		fGraphicsCard->HideSoftwareCursor(clipped);
813 
814 		fPainter->SetDrawState(d);
815 		BRect touched = filled ? fPainter->FillRoundRect(r, xrad, yrad)
816 							   : fPainter->StrokeRoundRect(r, xrad, yrad);
817 
818 		fGraphicsCard->Invalidate(touched);
819 		fGraphicsCard->ShowSoftwareCursor();
820 	}
821 }
822 
823 
824 void
825 DrawingEngine::DrawShape(const BRect& bounds, int32 opCount,
826 	const uint32* opList, int32 ptCount, const BPoint* ptList,
827 	const DrawState* d, bool filled)
828 {
829 	fGraphicsCard->HideSoftwareCursor();
830 
831 	fPainter->SetDrawState(d);
832 	BRect touched = fPainter->DrawShape(opCount, opList,
833 										ptCount, ptList,
834 										filled);
835 
836 	fGraphicsCard->Invalidate(touched);
837 	fGraphicsCard->ShowSoftwareCursor();
838 }
839 
840 
841 void
842 DrawingEngine::DrawTriangle(BPoint* pts, const BRect& bounds,
843 	const DrawState* d, bool filled)
844 {
845 	BRect clipped(bounds);
846 	if (!filled)
847 		extend_by_stroke_width(clipped, d);
848 	clipped = fPainter->ClipRect(clipped);
849 	if (clipped.IsValid()) {
850 		fGraphicsCard->HideSoftwareCursor(clipped);
851 
852 		fPainter->SetDrawState(d);
853 		if (filled)
854 			fPainter->FillTriangle(pts[0], pts[1], pts[2]);
855 		else
856 			fPainter->StrokeTriangle(pts[0], pts[1], pts[2]);
857 
858 		fGraphicsCard->Invalidate(clipped);
859 		fGraphicsCard->ShowSoftwareCursor();
860 	}
861 }
862 
863 // StrokeLine
864 void
865 DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end, DrawState* context)
866 {
867 	BRect touched(start, end);
868 	make_rect_valid(touched);
869 	extend_by_stroke_width(touched, context);
870 	touched = fPainter->ClipRect(touched);
871 	if (touched.IsValid()) {
872 		fGraphicsCard->HideSoftwareCursor(touched);
873 
874 		fPainter->SetDrawState(context);
875 		touched = fPainter->StrokeLine(start, end);
876 
877 		fGraphicsCard->Invalidate(touched);
878 		fGraphicsCard->ShowSoftwareCursor();
879 	}
880 }
881 
882 // StrokeLineArray
883 void
884 DrawingEngine::StrokeLineArray(int32 numLines,
885 	const LineArrayData *linedata, const DrawState *d)
886 {
887 	if (!d || !linedata || numLines <= 0)
888 		return;
889 
890 	// figure out bounding box for line array
891 	const LineArrayData *data = (const LineArrayData *)&(linedata[0]);
892 	BRect touched(min_c(data->pt1.x, data->pt2.x),
893 				  min_c(data->pt1.y, data->pt2.y),
894 				  max_c(data->pt1.x, data->pt2.x),
895 				  max_c(data->pt1.y, data->pt2.y));
896 
897 	for (int32 i = 1; i < numLines; i++) {
898 		data = (const LineArrayData *)&(linedata[i]);
899 		BRect box(min_c(data->pt1.x, data->pt2.x),
900 				  min_c(data->pt1.y, data->pt2.y),
901 				  max_c(data->pt1.x, data->pt2.x),
902 				  max_c(data->pt1.y, data->pt2.y));
903 		touched = touched | box;
904 	}
905 	extend_by_stroke_width(touched, d);
906 	touched = fPainter->ClipRect(touched);
907 	if (touched.IsValid()) {
908 		fGraphicsCard->HideSoftwareCursor(touched);
909 
910 		data = (const LineArrayData *)&(linedata[0]);
911 
912 		DrawState context;
913 		context.SetDrawingMode(d->GetDrawingMode());
914 		context.SetLowColor(d->LowColor());
915 		context.SetHighColor(data->color);
916 		context.SetPenSize(d->PenSize());
917 			// pen size is already correctly scaled
918 
919 		fPainter->SetDrawState(&context);
920 
921 		fPainter->StrokeLine(data->pt1, data->pt2);
922 
923 		for (int32 i = 1; i < numLines; i++) {
924 			data = (const LineArrayData *)&(linedata[i]);
925 			fPainter->SetHighColor(data->color);
926 			fPainter->StrokeLine(data->pt1, data->pt2);
927 		}
928 
929 		fGraphicsCard->Invalidate(touched);
930 		fGraphicsCard->ShowSoftwareCursor();
931 	}
932 }
933 
934 // #pragma mark -
935 
936 BPoint
937 DrawingEngine::DrawString(const char* string, int32 length,
938 						  const BPoint& pt, DrawState* d,
939 						  escapement_delta* delta)
940 {
941 // TODO: use delta
942 	FontLocker locker(d);
943 
944 	BPoint penLocation = pt;
945 
946 	fPainter->SetDrawState(d, true);
947 //bigtime_t now = system_time();
948 // TODO: BoundingBox is quite slow!! Optimizing it will be beneficial.
949 // Cursiously, the DrawString after it is actually faster!?!
950 // TODO: make the availability of the hardware cursor part of the
951 // HW acceleration flags and skip all calculations for HideSoftwareCursor
952 // in case we don't have one.
953 // TODO: Watch out about penLocation and use Painter::PenLocation() when
954 // not using BoundindBox anymore.
955 	BRect b = fPainter->BoundingBox(string, length, pt, &penLocation, delta);
956 	// stop here if we're supposed to render outside of the clipping
957 	b = fPainter->ClipRect(b);
958 	if (b.IsValid()) {
959 //printf("bounding box '%s': %lld µs\n", string, system_time() - now);
960 		fGraphicsCard->HideSoftwareCursor(b);
961 
962 //now = system_time();
963 		BRect touched = fPainter->DrawString(string, length, pt, delta);
964 //printf("drawing string: %lld µs\n", system_time() - now);
965 
966 		fGraphicsCard->Invalidate(touched);
967 		fGraphicsCard->ShowSoftwareCursor();
968 	}
969 
970 	return penLocation;
971 }
972 
973 // StringWidth
974 float
975 DrawingEngine::StringWidth(const char* string, int32 length,
976 						   const DrawState* d, escapement_delta* delta)
977 {
978 // TODO: use delta
979 	FontLocker locker(d);
980 
981 	float width = 0.0;
982 //	if (Lock()) {
983 // NOTE: For now it is enough to block on the
984 // font style lock, this already prevents multiple
985 // threads from executing this code and avoids a
986 // deadlock in case another thread holds the font
987 // lock already and then tries to lock the drawing
988 // engine after it is already locked here (race condition)
989 		width = fPainter->StringWidth(string, length, d);
990 //		Unlock();
991 //	}
992 	return width;
993 }
994 
995 // StringWidth
996 float
997 DrawingEngine::StringWidth(const char* string, int32 length,
998 						   const ServerFont& font, escapement_delta* delta)
999 {
1000 	DrawState d;
1001 	d.SetFont(font);
1002 	return StringWidth(string, length, &d, delta);
1003 }
1004 
1005 // StringHeight
1006 float
1007 DrawingEngine::StringHeight(const char *string, int32 length,
1008 							const DrawState *d)
1009 {
1010 	FontLocker locker(d);
1011 
1012 	float height = 0.0;
1013 //	if (Lock()) {
1014 // NOTE: For now it is enough to block on the
1015 // font style lock, this already prevents multiple
1016 // threads from executing this code and avoids a
1017 // deadlock in case another thread holds the font
1018 // lock already and then tries to lock the drawing
1019 // engine after it is already locked here (race condition)
1020 		fPainter->SetDrawState(d, true);
1021 		BPoint dummy1(0.0, 0.0);
1022 		BPoint dummy2(0.0, 0.0);
1023 		height = fPainter->BoundingBox(string, length, dummy1, &dummy2).Height();
1024 //		Unlock();
1025 //	}
1026 	return height;
1027 }
1028 
1029 // #pragma mark -
1030 
1031 // Lock
1032 bool
1033 DrawingEngine::Lock()
1034 {
1035 	return fGraphicsCard->WriteLock();
1036 }
1037 
1038 // Unlock
1039 void
1040 DrawingEngine::Unlock()
1041 {
1042 	fGraphicsCard->WriteUnlock();
1043 }
1044 
1045 // WriteLock
1046 bool
1047 DrawingEngine::WriteLock()
1048 {
1049 	return fGraphicsCard->WriteLock();
1050 }
1051 
1052 // WriteUnlock
1053 void
1054 DrawingEngine::WriteUnlock()
1055 {
1056 	fGraphicsCard->WriteUnlock();
1057 }
1058 
1059 // #pragma mark -
1060 
1061 // DumpToFile
1062 bool
1063 DrawingEngine::DumpToFile(const char *path)
1064 {
1065 	if (Lock()) {
1066 		RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer();
1067 		if (buffer) {
1068 			BRect bounds(0.0, 0.0, buffer->Width() - 1, buffer->Height() - 1);
1069 			SaveToPNG(path, bounds, buffer->ColorSpace(),
1070 					  buffer->Bits(),
1071 					  buffer->BitsLength(),
1072 					  buffer->BytesPerRow());
1073 		}
1074 		Unlock();
1075 	}
1076 	return true;
1077 }
1078 
1079 // DumpToBitmap
1080 ServerBitmap*
1081 DrawingEngine::DumpToBitmap()
1082 {
1083 	return NULL;
1084 }
1085 
1086 status_t
1087 DrawingEngine::ReadBitmap(ServerBitmap *bitmap, bool drawCursor, BRect bounds)
1088 {
1089 	if (Lock()) {
1090 		RenderingBuffer *buffer = fGraphicsCard->DrawingBuffer();
1091 		if (!buffer)
1092 			return B_ERROR;
1093 
1094 		BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1);
1095 		bounds = bounds & clip;
1096 		fGraphicsCard->HideSoftwareCursor(bounds);
1097 
1098 		status_t result = bitmap->ImportBits(buffer->Bits(), buffer->BitsLength(),
1099 			buffer->BytesPerRow(), buffer->ColorSpace(),
1100 			bounds.LeftTop(), BPoint(0, 0),
1101 			bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1);
1102 
1103 		if (drawCursor) {
1104 			ServerCursor *cursor = fGraphicsCard->Cursor();
1105 			int32 cursorWidth = cursor->Width();
1106 			int32 cursorHeight = cursor->Height();
1107 
1108 			BPoint cursorPosition = fGraphicsCard->GetCursorPosition();
1109 			cursorPosition -= bounds.LeftTop() + cursor->GetHotSpot();
1110 
1111 			BBitmap cursorArea(BRect(0, 0, cursorWidth - 1, cursorHeight - 1),
1112 				B_BITMAP_NO_SERVER_LINK, B_RGBA32);
1113 
1114 			cursorArea.ImportBits(bitmap->Bits(), bitmap->BitsLength(),
1115 				bitmap->BytesPerRow(), bitmap->ColorSpace(),
1116 				cursorPosition,	BPoint(0, 0),
1117 				cursorWidth, cursorHeight);
1118 
1119 			uint8 *bits = (uint8 *)cursorArea.Bits();
1120 			uint8 *cursorBits = (uint8 *)cursor->Bits();
1121 			for (int32 i = 0; i < cursorHeight; i++) {
1122 				for (int32 j = 0; j < cursorWidth; j++) {
1123 					uint8 alpha = 255 - cursorBits[3];
1124 					bits[0] = ((bits[0] * alpha) >> 8) + cursorBits[0];
1125 					bits[1] = ((bits[1] * alpha) >> 8) + cursorBits[1];
1126 					bits[2] = ((bits[2] * alpha) >> 8) + cursorBits[2];
1127 					cursorBits += 4;
1128 					bits += 4;
1129 				}
1130 			}
1131 
1132 			bitmap->ImportBits(cursorArea.Bits(), cursorArea.BitsLength(),
1133 				cursorArea.BytesPerRow(), cursorArea.ColorSpace(),
1134 				BPoint(0, 0), cursorPosition,
1135 				cursorWidth, cursorHeight);
1136 		}
1137 
1138 		fGraphicsCard->ShowSoftwareCursor();
1139 		Unlock();
1140 		return result;
1141 	}
1142 
1143 	return B_ERROR;
1144 }
1145 
1146 // #pragma mark -
1147 
1148 BRect
1149 DrawingEngine::_CopyRect(BRect src, int32 xOffset, int32 yOffset) const
1150 {
1151 	// TODO: assumes drawing buffer is 32 bits (which it currently always is)
1152 	BRect dst;
1153 	RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer();
1154 	if (buffer) {
1155 		BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1);
1156 
1157 		dst = src;
1158 		dst.OffsetBy(xOffset, yOffset);
1159 
1160 		if (clip.Intersects(src) && clip.Intersects(dst)) {
1161 			uint32 bytesPerRow = buffer->BytesPerRow();
1162 			uint8* bits = (uint8*)buffer->Bits();
1163 
1164 			// clip source rect
1165 			src = src & clip;
1166 			// clip dest rect
1167 			dst = dst & clip;
1168 			// move dest back over source and clip source to dest
1169 			dst.OffsetBy(-xOffset, -yOffset);
1170 			src = src & dst;
1171 
1172 			// calc offset in buffer
1173 			bits += (int32)src.left * 4 + (int32)src.top * bytesPerRow;
1174 
1175 			uint32 width = src.IntegerWidth() + 1;
1176 			uint32 height = src.IntegerHeight() + 1;
1177 
1178 			_CopyRect(bits, width, height, bytesPerRow, xOffset, yOffset);
1179 
1180 			// offset dest again, because it is return value
1181 			dst.OffsetBy(xOffset, yOffset);
1182 		}
1183 	}
1184 	return dst;
1185 }
1186 
1187 
1188 void
1189 DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height,
1190 	uint32 bytesPerRow, int32 xOffset, int32 yOffset) const
1191 {
1192 	// TODO: assumes drawing buffer is 32 bits (which it currently always is)
1193 	int32 xIncrement;
1194 	int32 yIncrement;
1195 
1196 	if (yOffset == 0 && xOffset > 0) {
1197 		// copy from right to left
1198 		xIncrement = -1;
1199 		src += (width - 1) * 4;
1200 	} else {
1201 		// copy from left to right
1202 		xIncrement = 1;
1203 	}
1204 
1205 	if (yOffset > 0) {
1206 		// copy from bottom to top
1207 		yIncrement = -bytesPerRow;
1208 		src += (height - 1) * bytesPerRow;
1209 	} else {
1210 		// copy from top to bottom
1211 		yIncrement = bytesPerRow;
1212 	}
1213 
1214 	uint8* dst = src + yOffset * bytesPerRow + xOffset * 4;
1215 
1216 	if (xIncrement == 1) {
1217 		uint8 tmpBuffer[width * 4];
1218 		for (uint32 y = 0; y < height; y++) {
1219 			// NOTE: read into temporary scanline buffer,
1220 			// avoid memcpy because it might be graphics card memory
1221 			gfxcpy32(tmpBuffer, src, width * 4);
1222 			// write back temporary scanline buffer
1223 			// NOTE: **don't read and write over the PCI bus
1224 			// at the same time**
1225 			memcpy(dst, tmpBuffer, width * 4);
1226 // NOTE: this (instead of the two pass copy above) might
1227 // speed up QEMU -> ?!? (would depend on how it emulates
1228 // the PCI bus...)
1229 // TODO: would be nice if we actually knew
1230 // if we're operating in graphics memory or main memory...
1231 //memcpy(dst, src, width * 4);
1232 			src += yIncrement;
1233 			dst += yIncrement;
1234 		}
1235 	} else {
1236 		for (uint32 y = 0; y < height; y++) {
1237 			uint32* srcHandle = (uint32*)src;
1238 			uint32* dstHandle = (uint32*)dst;
1239 			for (uint32 x = 0; x < width; x++) {
1240 				*dstHandle = *srcHandle;
1241 				srcHandle += xIncrement;
1242 				dstHandle += xIncrement;
1243 			}
1244 			src += yIncrement;
1245 			dst += yIncrement;
1246 		}
1247 	}
1248 }
1249 
1250 
1251