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