xref: /haiku/src/servers/app/drawing/DrawingEngine.cpp (revision 9ecf9d1c1d4888d341a6eac72112c72d1ae3a4cb)
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 		fPainter->AlignEllipseRect(&clipped, filled);
506 		if (!filled)
507 			extend_by_stroke_width(clipped, d);
508 
509 		clipped.left = floorf(clipped.left);
510 		clipped.top = floorf(clipped.top);
511 		clipped.right = ceilf(clipped.right);
512 		clipped.bottom = ceilf(clipped.bottom);
513 
514 		clipped = fPainter->ClipRect(clipped);
515 		if (clipped.IsValid()) {
516 			fGraphicsCard->HideSoftwareCursor(clipped);
517 
518 			fPainter->SetDrawState(d);
519 			fPainter->DrawEllipse(r, filled);
520 
521 			fGraphicsCard->Invalidate(clipped);
522 			fGraphicsCard->ShowSoftwareCursor();
523 		}
524 
525 		Unlock();
526 	}
527 }
528 
529 // DrawPolygon
530 void
531 DrawingEngine::DrawPolygon(BPoint* ptlist, int32 numpts,
532 						   BRect bounds, const DrawState* d,
533 						   bool filled, bool closed)
534 {
535 	if (Lock()) {
536 		make_rect_valid(bounds);
537 		if (!filled)
538 			extend_by_stroke_width(bounds, d);
539 		bounds = fPainter->ClipRect(bounds);
540 		if (bounds.IsValid()) {
541 			fGraphicsCard->HideSoftwareCursor(bounds);
542 
543 			fPainter->SetDrawState(d);
544 			fPainter->DrawPolygon(ptlist, numpts, filled, closed);
545 
546 			fGraphicsCard->Invalidate(bounds);
547 			fGraphicsCard->ShowSoftwareCursor();
548 		}
549 
550 		Unlock();
551 	}
552 }
553 
554 // #pragma mark - RGBColor
555 
556 void
557 DrawingEngine::StrokePoint(const BPoint& pt, const RGBColor &color)
558 {
559 	StrokeLine(pt, pt, color);
560 }
561 
562 // StrokeLine
563 //
564 // * this function is only used by Decorators
565 // * it assumes a one pixel wide line
566 void
567 DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end, const RGBColor &color)
568 {
569 	if (Lock()) {
570 		BRect touched(start, end);
571 		make_rect_valid(touched);
572 		touched = fPainter->ClipRect(touched);
573 		fGraphicsCard->HideSoftwareCursor(touched);
574 
575 		if (!fPainter->StraightLine(start, end, color.GetColor32())) {
576 			DrawState context;
577 			context.SetHighColor(color);
578 			context.SetDrawingMode(B_OP_OVER);
579 			StrokeLine(start, end, &context);
580 		} else {
581 			fGraphicsCard->Invalidate(touched);
582 		}
583 		fGraphicsCard->ShowSoftwareCursor();
584 		Unlock();
585 	}
586 }
587 
588 // this function is used to draw a one pixel wide rect
589 void
590 DrawingEngine::StrokeRect(BRect r, const RGBColor &color)
591 {
592 	if (Lock()) {
593 		make_rect_valid(r);
594 		BRect clipped = fPainter->ClipRect(r);
595 		if (clipped.IsValid()) {
596 			fGraphicsCard->HideSoftwareCursor(clipped);
597 
598 			fPainter->StrokeRect(r, color.GetColor32());
599 
600 			fGraphicsCard->Invalidate(clipped);
601 			fGraphicsCard->ShowSoftwareCursor();
602 		}
603 
604 		Unlock();
605 	}
606 }
607 
608 
609 void
610 DrawingEngine::FillRect(BRect r, const RGBColor& color)
611 {
612 	// NOTE: Write locking because we might use HW acceleration.
613 	// This needs to be investigated, I'm doing this because of
614 	// gut feeling.
615 	if (WriteLock()) {
616 		make_rect_valid(r);
617 		r = fPainter->ClipRect(r);
618 		if (r.IsValid()) {
619 			bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r);
620 
621 			// try hardware optimized version first
622 			if (fAvailableHWAccleration & HW_ACC_FILL_REGION) {
623 				BRegion region(r);
624 				region.IntersectWith(fPainter->ClippingRegion());
625 				fGraphicsCard->FillRegion(region, color,
626 										  fSuspendSyncLevel == 0
627 										  || cursorTouched);
628 			} else {
629 				fPainter->FillRect(r, color.GetColor32());
630 
631 				fGraphicsCard->Invalidate(r);
632 			}
633 
634 			if (cursorTouched)
635 				fGraphicsCard->ShowSoftwareCursor();
636 		}
637 
638 		WriteUnlock();
639 	}
640 }
641 
642 
643 void
644 DrawingEngine::FillRegion(BRegion& r, const RGBColor& color)
645 {
646 	// NOTE: Write locking because we might use HW acceleration.
647 	// This needs to be investigated, I'm doing this because of
648 	// gut feeling.
649 	// NOTE: region expected to be already clipped correctly!!
650 	if (WriteLock()) {
651 		BRect frame = r.Frame();
652 		bool cursorTouched = fGraphicsCard->HideSoftwareCursor(frame);
653 
654 		bool doInSoftware = true;
655 		// try hardware optimized version first
656 		if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0
657 			&& frame.Width() * frame.Height() > 100) {
658 			fGraphicsCard->FillRegion(r, color, fSuspendSyncLevel == 0
659 												|| cursorTouched);
660 			doInSoftware = false;
661 		}
662 
663 		if (doInSoftware) {
664 
665 			int32 count = r.CountRects();
666 			for (int32 i = 0; i < count; i++) {
667 				fPainter->FillRectNoClipping(r.RectAt(i), color.GetColor32());
668 			}
669 
670 			fGraphicsCard->Invalidate(r.Frame());
671 		}
672 
673 		if (cursorTouched)
674 			fGraphicsCard->ShowSoftwareCursor();
675 
676 		WriteUnlock();
677 	}
678 }
679 
680 // #pragma mark - DrawState
681 
682 void
683 DrawingEngine::StrokeRect(BRect r, const DrawState *d)
684 {
685 	// support invalid rects
686 	make_rect_valid(r);
687 	BRect clipped(r);
688 	extend_by_stroke_width(clipped, d);
689 	clipped = fPainter->ClipRect(clipped);
690 	if (clipped.IsValid()) {
691 
692 		fGraphicsCard->HideSoftwareCursor(clipped);
693 
694 		fPainter->SetDrawState(d);
695 		fPainter->StrokeRect(r);
696 
697 		fGraphicsCard->Invalidate(clipped);
698 		fGraphicsCard->ShowSoftwareCursor();
699 	}
700 }
701 
702 
703 void
704 DrawingEngine::FillRect(BRect r, const DrawState *d)
705 {
706 	// NOTE: Write locking because we might use HW acceleration.
707 	// This needs to be investigated, I'm doing this because of
708 	// gut feeling.
709 	if (WriteLock()) {
710 		make_rect_valid(r);
711 		r = fPainter->ClipRect(r);
712 		if (r.IsValid()) {
713 			bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r);
714 
715 			bool doInSoftware = true;
716 			if ((r.Width() + 1) * (r.Height() + 1) > 100.0) {
717 				// try hardware optimized version first
718 				// if the rect is large enough
719 				if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) {
720 					if (d->GetPattern() == B_SOLID_HIGH
721 						&& (d->GetDrawingMode() == B_OP_COPY
722 							|| d->GetDrawingMode() == B_OP_OVER)) {
723 						BRegion region(r);
724 						region.IntersectWith(fPainter->ClippingRegion());
725 						fGraphicsCard->FillRegion(region, d->HighColor(),
726 												  fSuspendSyncLevel == 0
727 												  || cursorTouched);
728 						doInSoftware = false;
729 					} else if (d->GetPattern() == B_SOLID_LOW
730 							   && d->GetDrawingMode() == B_OP_COPY) {
731 						BRegion region(r);
732 						region.IntersectWith(fPainter->ClippingRegion());
733 						fGraphicsCard->FillRegion(region, d->LowColor(),
734 												  fSuspendSyncLevel == 0
735 												  || cursorTouched);
736 						doInSoftware = false;
737 					}
738 				}
739 			}
740 			if (doInSoftware) {
741 				fPainter->SetDrawState(d);
742 				fPainter->FillRect(r);
743 
744 				fGraphicsCard->Invalidate(r);
745 			}
746 
747 			if (cursorTouched)
748 				fGraphicsCard->ShowSoftwareCursor();
749 		}
750 
751 		WriteUnlock();
752 	}
753 }
754 
755 
756 void
757 DrawingEngine::FillRegion(BRegion& r, const DrawState *d)
758 {
759 	// NOTE: Write locking because we might use HW acceleration.
760 	// This needs to be investigated, I'm doing this because of
761 	// gut feeling.
762 	if (WriteLock()) {
763 		BRect clipped = fPainter->ClipRect(r.Frame());
764 		if (clipped.IsValid()) {
765 			bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped);
766 
767 			bool doInSoftware = true;
768 			// try hardware optimized version first
769 			if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) {
770 				if (d->GetPattern() == B_SOLID_HIGH
771 					&& (d->GetDrawingMode() == B_OP_COPY
772 						|| d->GetDrawingMode() == B_OP_OVER)) {
773 					r.IntersectWith(fPainter->ClippingRegion());
774 					fGraphicsCard->FillRegion(r, d->HighColor(),
775 											  fSuspendSyncLevel == 0
776 											  || cursorTouched);
777 					doInSoftware = false;
778 				} else if (d->GetPattern() == B_SOLID_LOW
779 						   && d->GetDrawingMode() == B_OP_COPY) {
780 					r.IntersectWith(fPainter->ClippingRegion());
781 					fGraphicsCard->FillRegion(r, d->LowColor(),
782 											  fSuspendSyncLevel == 0
783 											  || cursorTouched);
784 					doInSoftware = false;
785 				}
786 			}
787 
788 			if (doInSoftware) {
789 				fPainter->SetDrawState(d);
790 
791 				BRect touched = fPainter->FillRect(r.RectAt(0));
792 
793 				int32 count = r.CountRects();
794 				for (int32 i = 1; i < count; i++) {
795 					touched = touched | fPainter->FillRect(r.RectAt(i));
796 				}
797 
798 				fGraphicsCard->Invalidate(touched);
799 			}
800 
801 			if (cursorTouched)
802 				fGraphicsCard->ShowSoftwareCursor();
803 		}
804 
805 		WriteUnlock();
806 	}
807 }
808 
809 
810 void
811 DrawingEngine::DrawRoundRect(BRect r, float xrad, float yrad,
812 	const DrawState* d, bool filled)
813 {
814 	// NOTE: the stroke does not extend past "r" in R5,
815 	// though I consider this unexpected behaviour.
816 	make_rect_valid(r);
817 	BRect clipped = fPainter->ClipRect(r);
818 
819 	clipped.left = floorf(clipped.left);
820 	clipped.top = floorf(clipped.top);
821 	clipped.right = ceilf(clipped.right);
822 	clipped.bottom = ceilf(clipped.bottom);
823 
824 	if (clipped.IsValid()) {
825 		fGraphicsCard->HideSoftwareCursor(clipped);
826 
827 		fPainter->SetDrawState(d);
828 		BRect touched = filled ? fPainter->FillRoundRect(r, xrad, yrad)
829 							   : fPainter->StrokeRoundRect(r, xrad, yrad);
830 
831 		fGraphicsCard->Invalidate(touched);
832 		fGraphicsCard->ShowSoftwareCursor();
833 	}
834 }
835 
836 
837 void
838 DrawingEngine::DrawShape(const BRect& bounds, int32 opCount,
839 	const uint32* opList, int32 ptCount, const BPoint* ptList,
840 	const DrawState* d, bool filled)
841 {
842 	// NOTE: hides cursor regardless of if and where
843 	// shape is drawn on screen, TODO: optimize
844 	fGraphicsCard->HideSoftwareCursor();
845 
846 	fPainter->SetDrawState(d);
847 	BRect touched = fPainter->DrawShape(opCount, opList,
848 										ptCount, ptList,
849 										filled);
850 
851 	fGraphicsCard->Invalidate(touched);
852 	fGraphicsCard->ShowSoftwareCursor();
853 }
854 
855 
856 void
857 DrawingEngine::DrawTriangle(BPoint* pts, const BRect& bounds,
858 	const DrawState* d, bool filled)
859 {
860 	BRect clipped(bounds);
861 	if (!filled)
862 		extend_by_stroke_width(clipped, d);
863 	clipped = fPainter->ClipRect(clipped);
864 	if (clipped.IsValid()) {
865 		fGraphicsCard->HideSoftwareCursor(clipped);
866 
867 		fPainter->SetDrawState(d);
868 		if (filled)
869 			fPainter->FillTriangle(pts[0], pts[1], pts[2]);
870 		else
871 			fPainter->StrokeTriangle(pts[0], pts[1], pts[2]);
872 
873 		fGraphicsCard->Invalidate(clipped);
874 		fGraphicsCard->ShowSoftwareCursor();
875 	}
876 }
877 
878 // StrokeLine
879 void
880 DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end, DrawState* context)
881 {
882 	BRect touched(start, end);
883 	make_rect_valid(touched);
884 	extend_by_stroke_width(touched, context);
885 	touched = fPainter->ClipRect(touched);
886 	if (touched.IsValid()) {
887 		fGraphicsCard->HideSoftwareCursor(touched);
888 
889 		fPainter->SetDrawState(context);
890 		touched = fPainter->StrokeLine(start, end);
891 
892 		fGraphicsCard->Invalidate(touched);
893 		fGraphicsCard->ShowSoftwareCursor();
894 	}
895 }
896 
897 // StrokeLineArray
898 void
899 DrawingEngine::StrokeLineArray(int32 numLines,
900 	const LineArrayData *linedata, const DrawState *d)
901 {
902 	if (!d || !linedata || numLines <= 0)
903 		return;
904 
905 	// figure out bounding box for line array
906 	const LineArrayData *data = (const LineArrayData *)&(linedata[0]);
907 	BRect touched(min_c(data->pt1.x, data->pt2.x),
908 				  min_c(data->pt1.y, data->pt2.y),
909 				  max_c(data->pt1.x, data->pt2.x),
910 				  max_c(data->pt1.y, data->pt2.y));
911 
912 	for (int32 i = 1; i < numLines; i++) {
913 		data = (const LineArrayData *)&(linedata[i]);
914 		BRect box(min_c(data->pt1.x, data->pt2.x),
915 				  min_c(data->pt1.y, data->pt2.y),
916 				  max_c(data->pt1.x, data->pt2.x),
917 				  max_c(data->pt1.y, data->pt2.y));
918 		touched = touched | box;
919 	}
920 	extend_by_stroke_width(touched, d);
921 	touched = fPainter->ClipRect(touched);
922 	if (touched.IsValid()) {
923 		fGraphicsCard->HideSoftwareCursor(touched);
924 
925 		data = (const LineArrayData *)&(linedata[0]);
926 
927 		DrawState context;
928 		context.SetDrawingMode(d->GetDrawingMode());
929 		context.SetLowColor(d->LowColor());
930 		context.SetHighColor(data->color);
931 		context.SetPenSize(d->PenSize());
932 			// pen size is already correctly scaled
933 
934 		fPainter->SetDrawState(&context);
935 
936 		fPainter->StrokeLine(data->pt1, data->pt2);
937 
938 		for (int32 i = 1; i < numLines; i++) {
939 			data = (const LineArrayData *)&(linedata[i]);
940 			fPainter->SetHighColor(data->color);
941 			fPainter->StrokeLine(data->pt1, data->pt2);
942 		}
943 
944 		fGraphicsCard->Invalidate(touched);
945 		fGraphicsCard->ShowSoftwareCursor();
946 	}
947 }
948 
949 // #pragma mark -
950 
951 BPoint
952 DrawingEngine::DrawString(const char* string, int32 length,
953 						  const BPoint& pt, DrawState* d,
954 						  escapement_delta* delta)
955 {
956 // TODO: use delta
957 	FontLocker locker(d);
958 
959 	BPoint penLocation = pt;
960 
961 	fPainter->SetDrawState(d, true);
962 //bigtime_t now = system_time();
963 // TODO: BoundingBox is quite slow!! Optimizing it will be beneficial.
964 // Cursiously, the DrawString after it is actually faster!?!
965 // TODO: make the availability of the hardware cursor part of the
966 // HW acceleration flags and skip all calculations for HideSoftwareCursor
967 // in case we don't have one.
968 // TODO: Watch out about penLocation and use Painter::PenLocation() when
969 // not using BoundindBox anymore.
970 	BRect b = fPainter->BoundingBox(string, length, pt, &penLocation, delta);
971 	// stop here if we're supposed to render outside of the clipping
972 	b = fPainter->ClipRect(b);
973 	if (b.IsValid()) {
974 //printf("bounding box '%s': %lld µs\n", string, system_time() - now);
975 		fGraphicsCard->HideSoftwareCursor(b);
976 
977 //now = system_time();
978 		BRect touched = fPainter->DrawString(string, length, pt, delta);
979 //printf("drawing string: %lld µs\n", system_time() - now);
980 
981 		fGraphicsCard->Invalidate(touched);
982 		fGraphicsCard->ShowSoftwareCursor();
983 	}
984 
985 	return penLocation;
986 }
987 
988 // StringWidth
989 float
990 DrawingEngine::StringWidth(const char* string, int32 length,
991 						   const DrawState* d, escapement_delta* delta)
992 {
993 // TODO: use delta
994 	FontLocker locker(d);
995 
996 	float width = 0.0;
997 //	if (Lock()) {
998 // NOTE: For now it is enough to block on the
999 // font style lock, this already prevents multiple
1000 // threads from executing this code and avoids a
1001 // deadlock in case another thread holds the font
1002 // lock already and then tries to lock the drawing
1003 // engine after it is already locked here (race condition)
1004 		width = fPainter->StringWidth(string, length, d);
1005 //		Unlock();
1006 //	}
1007 	return width;
1008 }
1009 
1010 // StringWidth
1011 float
1012 DrawingEngine::StringWidth(const char* string, int32 length,
1013 						   const ServerFont& font, escapement_delta* delta)
1014 {
1015 	DrawState d;
1016 	d.SetFont(font);
1017 	return StringWidth(string, length, &d, delta);
1018 }
1019 
1020 // StringHeight
1021 float
1022 DrawingEngine::StringHeight(const char *string, int32 length,
1023 							const DrawState *d)
1024 {
1025 	FontLocker locker(d);
1026 
1027 	float height = 0.0;
1028 //	if (Lock()) {
1029 // NOTE: For now it is enough to block on the
1030 // font style lock, this already prevents multiple
1031 // threads from executing this code and avoids a
1032 // deadlock in case another thread holds the font
1033 // lock already and then tries to lock the drawing
1034 // engine after it is already locked here (race condition)
1035 		fPainter->SetDrawState(d, true);
1036 		BPoint dummy1(0.0, 0.0);
1037 		BPoint dummy2(0.0, 0.0);
1038 		height = fPainter->BoundingBox(string, length, dummy1, &dummy2).Height();
1039 //		Unlock();
1040 //	}
1041 	return height;
1042 }
1043 
1044 // #pragma mark -
1045 
1046 // Lock
1047 bool
1048 DrawingEngine::Lock()
1049 {
1050 	return fGraphicsCard->WriteLock();
1051 }
1052 
1053 // Unlock
1054 void
1055 DrawingEngine::Unlock()
1056 {
1057 	fGraphicsCard->WriteUnlock();
1058 }
1059 
1060 // WriteLock
1061 bool
1062 DrawingEngine::WriteLock()
1063 {
1064 	return fGraphicsCard->WriteLock();
1065 }
1066 
1067 // WriteUnlock
1068 void
1069 DrawingEngine::WriteUnlock()
1070 {
1071 	fGraphicsCard->WriteUnlock();
1072 }
1073 
1074 // #pragma mark -
1075 
1076 // DumpToFile
1077 bool
1078 DrawingEngine::DumpToFile(const char *path)
1079 {
1080 	if (Lock()) {
1081 		RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer();
1082 		if (buffer) {
1083 			BRect bounds(0.0, 0.0, buffer->Width() - 1, buffer->Height() - 1);
1084 			SaveToPNG(path, bounds, buffer->ColorSpace(),
1085 					  buffer->Bits(),
1086 					  buffer->BitsLength(),
1087 					  buffer->BytesPerRow());
1088 		}
1089 		Unlock();
1090 	}
1091 	return true;
1092 }
1093 
1094 // DumpToBitmap
1095 ServerBitmap*
1096 DrawingEngine::DumpToBitmap()
1097 {
1098 	return NULL;
1099 }
1100 
1101 status_t
1102 DrawingEngine::ReadBitmap(ServerBitmap *bitmap, bool drawCursor, BRect bounds)
1103 {
1104 	if (Lock()) {
1105 		RenderingBuffer *buffer = fGraphicsCard->DrawingBuffer();
1106 		if (!buffer)
1107 			return B_ERROR;
1108 
1109 		BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1);
1110 		bounds = bounds & clip;
1111 		fGraphicsCard->HideSoftwareCursor(bounds);
1112 
1113 		status_t result = bitmap->ImportBits(buffer->Bits(), buffer->BitsLength(),
1114 			buffer->BytesPerRow(), buffer->ColorSpace(),
1115 			bounds.LeftTop(), BPoint(0, 0),
1116 			bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1);
1117 
1118 		if (drawCursor) {
1119 			ServerCursor *cursor = fGraphicsCard->Cursor();
1120 			int32 cursorWidth = cursor->Width();
1121 			int32 cursorHeight = cursor->Height();
1122 
1123 			BPoint cursorPosition = fGraphicsCard->CursorPosition();
1124 			cursorPosition -= bounds.LeftTop() + cursor->GetHotSpot();
1125 
1126 			BBitmap cursorArea(BRect(0, 0, cursorWidth - 1, cursorHeight - 1),
1127 				B_BITMAP_NO_SERVER_LINK, B_RGBA32);
1128 
1129 			cursorArea.ImportBits(bitmap->Bits(), bitmap->BitsLength(),
1130 				bitmap->BytesPerRow(), bitmap->ColorSpace(),
1131 				cursorPosition,	BPoint(0, 0),
1132 				cursorWidth, cursorHeight);
1133 
1134 			uint8 *bits = (uint8 *)cursorArea.Bits();
1135 			uint8 *cursorBits = (uint8 *)cursor->Bits();
1136 			for (int32 i = 0; i < cursorHeight; i++) {
1137 				for (int32 j = 0; j < cursorWidth; j++) {
1138 					uint8 alpha = 255 - cursorBits[3];
1139 					bits[0] = ((bits[0] * alpha) >> 8) + cursorBits[0];
1140 					bits[1] = ((bits[1] * alpha) >> 8) + cursorBits[1];
1141 					bits[2] = ((bits[2] * alpha) >> 8) + cursorBits[2];
1142 					cursorBits += 4;
1143 					bits += 4;
1144 				}
1145 			}
1146 
1147 			bitmap->ImportBits(cursorArea.Bits(), cursorArea.BitsLength(),
1148 				cursorArea.BytesPerRow(), cursorArea.ColorSpace(),
1149 				BPoint(0, 0), cursorPosition,
1150 				cursorWidth, cursorHeight);
1151 		}
1152 
1153 		fGraphicsCard->ShowSoftwareCursor();
1154 		Unlock();
1155 		return result;
1156 	}
1157 
1158 	return B_ERROR;
1159 }
1160 
1161 // #pragma mark -
1162 
1163 BRect
1164 DrawingEngine::_CopyRect(BRect src, int32 xOffset, int32 yOffset) const
1165 {
1166 	// TODO: assumes drawing buffer is 32 bits (which it currently always is)
1167 	BRect dst;
1168 	RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer();
1169 	if (buffer) {
1170 		BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1);
1171 
1172 		dst = src;
1173 		dst.OffsetBy(xOffset, yOffset);
1174 
1175 		if (clip.Intersects(src) && clip.Intersects(dst)) {
1176 			uint32 bytesPerRow = buffer->BytesPerRow();
1177 			uint8* bits = (uint8*)buffer->Bits();
1178 
1179 			// clip source rect
1180 			src = src & clip;
1181 			// clip dest rect
1182 			dst = dst & clip;
1183 			// move dest back over source and clip source to dest
1184 			dst.OffsetBy(-xOffset, -yOffset);
1185 			src = src & dst;
1186 
1187 			// calc offset in buffer
1188 			bits += (int32)src.left * 4 + (int32)src.top * bytesPerRow;
1189 
1190 			uint32 width = src.IntegerWidth() + 1;
1191 			uint32 height = src.IntegerHeight() + 1;
1192 
1193 			_CopyRect(bits, width, height, bytesPerRow, xOffset, yOffset);
1194 
1195 			// offset dest again, because it is return value
1196 			dst.OffsetBy(xOffset, yOffset);
1197 		}
1198 	}
1199 	return dst;
1200 }
1201 
1202 
1203 void
1204 DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height,
1205 	uint32 bytesPerRow, int32 xOffset, int32 yOffset) const
1206 {
1207 	// TODO: assumes drawing buffer is 32 bits (which it currently always is)
1208 	int32 xIncrement;
1209 	int32 yIncrement;
1210 
1211 	if (yOffset == 0 && xOffset > 0) {
1212 		// copy from right to left
1213 		xIncrement = -1;
1214 		src += (width - 1) * 4;
1215 	} else {
1216 		// copy from left to right
1217 		xIncrement = 1;
1218 	}
1219 
1220 	if (yOffset > 0) {
1221 		// copy from bottom to top
1222 		yIncrement = -bytesPerRow;
1223 		src += (height - 1) * bytesPerRow;
1224 	} else {
1225 		// copy from top to bottom
1226 		yIncrement = bytesPerRow;
1227 	}
1228 
1229 	uint8* dst = src + yOffset * bytesPerRow + xOffset * 4;
1230 
1231 	if (xIncrement == 1) {
1232 		uint8 tmpBuffer[width * 4];
1233 		for (uint32 y = 0; y < height; y++) {
1234 			// NOTE: read into temporary scanline buffer,
1235 			// avoid memcpy because it might be graphics card memory
1236 			gfxcpy32(tmpBuffer, src, width * 4);
1237 			// write back temporary scanline buffer
1238 			// NOTE: **don't read and write over the PCI bus
1239 			// at the same time**
1240 			memcpy(dst, tmpBuffer, width * 4);
1241 // NOTE: this (instead of the two pass copy above) might
1242 // speed up QEMU -> ?!? (would depend on how it emulates
1243 // the PCI bus...)
1244 // TODO: would be nice if we actually knew
1245 // if we're operating in graphics memory or main memory...
1246 //memcpy(dst, src, width * 4);
1247 			src += yIncrement;
1248 			dst += yIncrement;
1249 		}
1250 	} else {
1251 		for (uint32 y = 0; y < height; y++) {
1252 			uint32* srcHandle = (uint32*)src;
1253 			uint32* dstHandle = (uint32*)dst;
1254 			for (uint32 x = 0; x < width; x++) {
1255 				*dstHandle = *srcHandle;
1256 				srcHandle += xIncrement;
1257 				dstHandle += xIncrement;
1258 			}
1259 			src += yIncrement;
1260 			dst += yIncrement;
1261 		}
1262 	}
1263 }
1264 
1265 
1266