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