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