xref: /haiku/src/servers/app/DrawState.cpp (revision 37fedaf8494b34aad811abcc49e79aa32943f880)
1 /*
2  * Copyright 2001-2008, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  *		Adi Oanca <adioanca@mymail.ro>
8  *		Stephan Aßmus <superstippi@gmx.de>
9  *		Axel Dörfler, axeld@pinc-software.de
10  *		Michael Pfeiffer <laplace@users.sourceforge.net>
11  */
12 
13 //!	Data classes for working with BView states and draw parameters
14 
15 #include "DrawState.h"
16 
17 #include <new>
18 #include <stdio.h>
19 
20 #include <Region.h>
21 
22 #include "AlphaMask.h"
23 #include "LinkReceiver.h"
24 #include "LinkSender.h"
25 #include "ServerProtocolStructs.h"
26 
27 
28 using std::nothrow;
29 
30 
31 DrawState::DrawState()
32 	:
33 	fOrigin(0.0f, 0.0f),
34 	fCombinedOrigin(0.0f, 0.0f),
35 	fScale(1.0f),
36 	fCombinedScale(1.0f),
37 	fTransform(),
38 	fCombinedTransform(),
39 	fClippingRegion(NULL),
40 	fAlphaMask(NULL),
41 
42 	fHighColor((rgb_color){ 0, 0, 0, 255 }),
43 	fLowColor((rgb_color){ 255, 255, 255, 255 }),
44 	fPattern(kSolidHigh),
45 
46 	fDrawingMode(B_OP_COPY),
47 	fAlphaSrcMode(B_PIXEL_ALPHA),
48 	fAlphaFncMode(B_ALPHA_OVERLAY),
49 
50 	fPenLocation(0.0f, 0.0f),
51 	fPenSize(1.0f),
52 
53 	fFontAliasing(false),
54 	fSubPixelPrecise(false),
55 	fLineCapMode(B_BUTT_CAP),
56 	fLineJoinMode(B_MITER_JOIN),
57 	fMiterLimit(B_DEFAULT_MITER_LIMIT),
58 	fFillRule(B_NONZERO),
59 	fPreviousState(NULL)
60 {
61 	fUnscaledFontSize = fFont.Size();
62 }
63 
64 
65 DrawState::DrawState(const DrawState& other)
66 	:
67 	fOrigin(other.fOrigin),
68 	fCombinedOrigin(other.fCombinedOrigin),
69 	fScale(other.fScale),
70 	fCombinedScale(other.fCombinedScale),
71 	fTransform(other.fTransform),
72 	fCombinedTransform(other.fCombinedTransform),
73 	fClippingRegion(NULL),
74 	fAlphaMask(NULL),
75 
76 	fHighColor(other.fHighColor),
77 	fLowColor(other.fLowColor),
78 	fPattern(other.fPattern),
79 
80 	fDrawingMode(other.fDrawingMode),
81 	fAlphaSrcMode(other.fAlphaSrcMode),
82 	fAlphaFncMode(other.fAlphaFncMode),
83 
84 	fPenLocation(other.fPenLocation),
85 	fPenSize(other.fPenSize),
86 
87 	fFont(other.fFont),
88 	fFontAliasing(other.fFontAliasing),
89 
90 	fSubPixelPrecise(other.fSubPixelPrecise),
91 
92 	fLineCapMode(other.fLineCapMode),
93 	fLineJoinMode(other.fLineJoinMode),
94 	fMiterLimit(other.fMiterLimit),
95 	fFillRule(other.fFillRule),
96 
97 	// Since fScale is reset to 1.0, the unscaled
98 	// font size is the current size of the font
99 	// (which is from->fUnscaledFontSize * from->fCombinedScale)
100 	fUnscaledFontSize(other.fUnscaledFontSize),
101 	fPreviousState(NULL)
102 {
103 }
104 
105 
106 DrawState::~DrawState()
107 {
108 	delete fClippingRegion;
109 	delete fPreviousState;
110 	if (fAlphaMask != NULL)
111 		fAlphaMask->ReleaseReference();
112 }
113 
114 
115 DrawState*
116 DrawState::PushState()
117 {
118 	DrawState* next = new (nothrow) DrawState(*this);
119 
120 	if (next != NULL) {
121 		// Prepare state as derived from this state
122 		next->fOrigin = BPoint(0.0, 0.0);
123 		next->fScale = 1.0;
124 		next->fTransform.Reset();
125 		next->fPreviousState = this;
126 		next->SetAlphaMask(fAlphaMask);
127 	}
128 
129 	return next;
130 }
131 
132 
133 DrawState*
134 DrawState::PopState()
135 {
136 	DrawState* previous = PreviousState();
137 
138 	fPreviousState = NULL;
139 	delete this;
140 
141 	return previous;
142 }
143 
144 
145 void
146 DrawState::ReadFontFromLink(BPrivate::LinkReceiver& link)
147 {
148 	uint16 mask;
149 	link.Read<uint16>(&mask);
150 
151 	if ((mask & B_FONT_FAMILY_AND_STYLE) != 0) {
152 		uint32 fontID;
153 		link.Read<uint32>(&fontID);
154 		fFont.SetFamilyAndStyle(fontID);
155 	}
156 
157 	if ((mask & B_FONT_SIZE) != 0) {
158 		float size;
159 		link.Read<float>(&size);
160 		fUnscaledFontSize = size;
161 		fFont.SetSize(fUnscaledFontSize * fCombinedScale);
162 	}
163 
164 	if ((mask & B_FONT_SHEAR) != 0) {
165 		float shear;
166 		link.Read<float>(&shear);
167 		fFont.SetShear(shear);
168 	}
169 
170 	if ((mask & B_FONT_ROTATION) != 0) {
171 		float rotation;
172 		link.Read<float>(&rotation);
173 		fFont.SetRotation(rotation);
174 	}
175 
176 	if ((mask & B_FONT_FALSE_BOLD_WIDTH) != 0) {
177 		float falseBoldWidth;
178 		link.Read<float>(&falseBoldWidth);
179 		fFont.SetFalseBoldWidth(falseBoldWidth);
180 	}
181 
182 	if ((mask & B_FONT_SPACING) != 0) {
183 		uint8 spacing;
184 		link.Read<uint8>(&spacing);
185 		fFont.SetSpacing(spacing);
186 	}
187 
188 	if ((mask & B_FONT_ENCODING) != 0) {
189 		uint8 encoding;
190 		link.Read<uint8>(&encoding);
191 		fFont.SetEncoding(encoding);
192 	}
193 
194 	if ((mask & B_FONT_FACE) != 0) {
195 		uint16 face;
196 		link.Read<uint16>(&face);
197 		fFont.SetFace(face);
198 	}
199 
200 	if ((mask & B_FONT_FLAGS) != 0) {
201 		uint32 flags;
202 		link.Read<uint32>(&flags);
203 		fFont.SetFlags(flags);
204 	}
205 }
206 
207 
208 void
209 DrawState::ReadFromLink(BPrivate::LinkReceiver& link)
210 {
211 	ViewSetStateInfo info;
212 
213 	link.Read<ViewSetStateInfo>(&info);
214 
215 	fPenLocation = info.penLocation;
216 	fPenSize = info.penSize;
217 	fHighColor = info.highColor;
218 	fLowColor = info.lowColor;
219 	fPattern = info.pattern;
220 	fDrawingMode = info.drawingMode;
221 	fOrigin = info.origin;
222 	fScale = info.scale;
223 	fTransform = info.transform;
224 	fLineJoinMode = info.lineJoin;
225 	fLineCapMode = info.lineCap;
226 	fMiterLimit = info.miterLimit;
227 	fFillRule = info.fillRule;
228 	fAlphaSrcMode = info.alphaSourceMode;
229 	fAlphaFncMode = info.alphaFunctionMode;
230 	fFontAliasing = info.fontAntialiasing;
231 
232 	if (fPreviousState != NULL) {
233 		fCombinedOrigin = fPreviousState->fCombinedOrigin + fOrigin;
234 		fCombinedScale = fPreviousState->fCombinedScale * fScale;
235 		fCombinedTransform = fPreviousState->fCombinedTransform * fTransform;
236 	} else {
237 		fCombinedOrigin = fOrigin;
238 		fCombinedScale = fScale;
239 		fCombinedTransform = fTransform;
240 	}
241 
242 
243 	// read clipping
244 	// TODO: This could be optimized, but the user clipping regions are rarely
245 	// used, so it's low priority...
246 	int32 clipRectCount;
247 	link.Read<int32>(&clipRectCount);
248 
249 	if (clipRectCount >= 0) {
250 		BRegion region;
251 		BRect rect;
252 		for (int32 i = 0; i < clipRectCount; i++) {
253 			link.Read<BRect>(&rect);
254 			region.Include(rect);
255 		}
256 		SetClippingRegion(&region);
257 	} else {
258 		// No user clipping used
259 		SetClippingRegion(NULL);
260 	}
261 }
262 
263 
264 void
265 DrawState::WriteToLink(BPrivate::LinkSender& link) const
266 {
267 	// Attach font state
268 	ViewGetStateInfo info;
269 	info.fontID = fFont.GetFamilyAndStyle();
270 	info.fontSize = fFont.Size();
271 	info.fontShear = fFont.Shear();
272 	info.fontRotation = fFont.Rotation();
273 	info.fontFalseBoldWidth = fFont.FalseBoldWidth();
274 	info.fontSpacing = fFont.Spacing();
275 	info.fontEncoding = fFont.Encoding();
276 	info.fontFace = fFont.Face();
277 	info.fontFlags = fFont.Flags();
278 
279 	// Attach view state
280 	info.viewStateInfo.penLocation = fPenLocation;
281 	info.viewStateInfo.penSize = fPenSize;
282 	info.viewStateInfo.highColor = fHighColor;
283 	info.viewStateInfo.lowColor = fLowColor;
284 	info.viewStateInfo.pattern = (::pattern)fPattern.GetPattern();
285 	info.viewStateInfo.drawingMode = fDrawingMode;
286 	info.viewStateInfo.origin = fOrigin;
287 	info.viewStateInfo.scale = fScale;
288 	info.viewStateInfo.transform = fTransform;
289 	info.viewStateInfo.lineJoin = fLineJoinMode;
290 	info.viewStateInfo.lineCap = fLineCapMode;
291 	info.viewStateInfo.miterLimit = fMiterLimit;
292 	info.viewStateInfo.fillRule = fFillRule;
293 	info.viewStateInfo.alphaSourceMode = fAlphaSrcMode;
294 	info.viewStateInfo.alphaFunctionMode = fAlphaFncMode;
295 	info.viewStateInfo.fontAntialiasing = fFontAliasing;
296 
297 
298 	link.Attach<ViewGetStateInfo>(info);
299 
300 
301 	// TODO: Could be optimized, but is low prio, since most views do not
302 	// use a custom clipping region...
303 	if (fClippingRegion != NULL) {
304 		int32 clippingRectCount = fClippingRegion->CountRects();
305 		link.Attach<int32>(clippingRectCount);
306 		for (int i = 0; i < clippingRectCount; i++)
307 			link.Attach<BRect>(fClippingRegion->RectAt(i));
308 	} else {
309 		// no client clipping
310 		link.Attach<int32>(-1);
311 	}
312 }
313 
314 
315 void
316 DrawState::SetOrigin(BPoint origin)
317 {
318 	fOrigin = origin;
319 
320 	// NOTE: the origins of earlier states are never expected to
321 	// change, only the topmost state ever changes
322 	if (fPreviousState != NULL) {
323 		fCombinedOrigin.x = fPreviousState->fCombinedOrigin.x
324 			+ fOrigin.x * fPreviousState->fCombinedScale;
325 		fCombinedOrigin.y = fPreviousState->fCombinedOrigin.y
326 			+ fOrigin.y * fPreviousState->fCombinedScale;
327 	} else {
328 		fCombinedOrigin = fOrigin;
329 	}
330 }
331 
332 
333 void
334 DrawState::SetScale(float scale)
335 {
336 	if (fScale == scale)
337 		return;
338 
339 	fScale = scale;
340 
341 	// NOTE: the scales of earlier states are never expected to
342 	// change, only the topmost state ever changes
343 	if (fPreviousState != NULL)
344 		fCombinedScale = fPreviousState->fCombinedScale * fScale;
345 	else
346 		fCombinedScale = fScale;
347 
348 	// update font size
349 	// NOTE: This is what makes the call potentially expensive,
350 	// hence the introductory check
351 	fFont.SetSize(fUnscaledFontSize * fCombinedScale);
352 }
353 
354 
355 void
356 DrawState::SetTransform(BAffineTransform transform)
357 {
358 	if (fTransform == transform)
359 		return;
360 
361 	fTransform = transform;
362 
363 	// NOTE: the transforms of earlier states are never expected to
364 	// change, only the topmost state ever changes
365 	if (fPreviousState != NULL)
366 		fCombinedTransform = fPreviousState->fCombinedTransform * fTransform;
367 	else
368 		fCombinedTransform = fTransform;
369 }
370 
371 
372 void
373 DrawState::SetClippingRegion(const BRegion* region)
374 {
375 	if (region) {
376 		if (fClippingRegion != NULL)
377 			*fClippingRegion = *region;
378 		else
379 			fClippingRegion = new(nothrow) BRegion(*region);
380 	} else {
381 		delete fClippingRegion;
382 		fClippingRegion = NULL;
383 	}
384 }
385 
386 
387 bool
388 DrawState::HasClipping() const
389 {
390 	if (fClippingRegion != NULL)
391 		return true;
392 	if (fPreviousState != NULL)
393 		return fPreviousState->HasClipping();
394 	return false;
395 }
396 
397 
398 bool
399 DrawState::HasAdditionalClipping() const
400 {
401 	return fClippingRegion != NULL;
402 }
403 
404 
405 bool
406 DrawState::GetCombinedClippingRegion(BRegion* region) const
407 {
408 	if (fClippingRegion != NULL) {
409 		BRegion localTransformedClipping(*fClippingRegion);
410 		Transform(&localTransformedClipping);
411 
412 		if (fPreviousState != NULL
413 			&& fPreviousState->GetCombinedClippingRegion(region)) {
414 			localTransformedClipping.IntersectWith(region);
415 		}
416 		*region = localTransformedClipping;
417 		return true;
418 	} else {
419 		if (fPreviousState != NULL)
420 			return fPreviousState->GetCombinedClippingRegion(region);
421 	}
422 	return false;
423 }
424 
425 
426 void
427 DrawState::SetAlphaMask(AlphaMask* mask)
428 {
429 	// NOTE: In BeOS, it wasn't possible to clip to a BPicture and keep
430 	// regular custom clipping to a BRegion at the same time.
431 	if (fAlphaMask == mask)
432 		return;
433 
434 	if (mask != NULL)
435 		mask->AcquireReference();
436 	if (fAlphaMask != NULL)
437 		fAlphaMask->ReleaseReference();
438 	fAlphaMask = mask;
439 	if (fAlphaMask != NULL && fPreviousState != NULL)
440 		fAlphaMask->SetPrevious(fPreviousState->fAlphaMask);
441 
442 }
443 
444 
445 AlphaMask*
446 DrawState::GetAlphaMask() const
447 {
448 	return fAlphaMask;
449 }
450 
451 
452 // #pragma mark -
453 
454 
455 void
456 DrawState::Transform(float* x, float* y) const
457 {
458 	// scale relative to origin, therefore
459 	// scale first then translate to
460 	// origin
461 	*x *= fCombinedScale;
462 	*y *= fCombinedScale;
463 	*x += fCombinedOrigin.x;
464 	*y += fCombinedOrigin.y;
465 }
466 
467 
468 void
469 DrawState::InverseTransform(float* x, float* y) const
470 {
471 	*x -= fCombinedOrigin.x;
472 	*y -= fCombinedOrigin.y;
473 	if (fCombinedScale != 0.0) {
474 		*x /= fCombinedScale;
475 		*y /= fCombinedScale;
476 	}
477 }
478 
479 
480 void
481 DrawState::Transform(BPoint* point) const
482 {
483 	Transform(&(point->x), &(point->y));
484 }
485 
486 
487 void
488 DrawState::Transform(BRect* rect) const
489 {
490 	Transform(&(rect->left), &(rect->top));
491 	Transform(&(rect->right), &(rect->bottom));
492 }
493 
494 
495 void
496 DrawState::Transform(BRegion* region) const
497 {
498 	if (fCombinedScale == 1.0) {
499 		region->OffsetBy(fCombinedOrigin.x, fCombinedOrigin.y);
500 	} else {
501 		// TODO: optimize some more
502 		BRegion converted;
503 		int32 count = region->CountRects();
504 		for (int32 i = 0; i < count; i++) {
505 			BRect r = region->RectAt(i);
506 			BPoint lt(r.LeftTop());
507 			BPoint rb(r.RightBottom());
508 			// offset to bottom right corner of pixel before transformation
509 			rb.x++;
510 			rb.y++;
511 			// apply transformation
512 			Transform(&lt.x, &lt.y);
513 			Transform(&rb.x, &rb.y);
514 			// reset bottom right to pixel "index"
515 			rb.x--;
516 			rb.y--;
517 			// add rect to converted region
518 			// NOTE/TODO: the rect would not have to go
519 			// through the whole intersection test process,
520 			// it is guaranteed not to overlap with any rect
521 			// already contained in the region
522 			converted.Include(BRect(lt, rb));
523 		}
524 		*region = converted;
525 	}
526 }
527 
528 
529 void
530 DrawState::InverseTransform(BPoint* point) const
531 {
532 	InverseTransform(&(point->x), &(point->y));
533 }
534 
535 
536 // #pragma mark -
537 
538 
539 void
540 DrawState::SetHighColor(rgb_color color)
541 {
542 	fHighColor = color;
543 }
544 
545 
546 void
547 DrawState::SetLowColor(rgb_color color)
548 {
549 	fLowColor = color;
550 }
551 
552 
553 void
554 DrawState::SetPattern(const Pattern& pattern)
555 {
556 	fPattern = pattern;
557 }
558 
559 
560 void
561 DrawState::SetDrawingMode(drawing_mode mode)
562 {
563 	fDrawingMode = mode;
564 }
565 
566 
567 void
568 DrawState::SetBlendingMode(source_alpha srcMode, alpha_function fncMode)
569 {
570 	fAlphaSrcMode = srcMode;
571 	fAlphaFncMode = fncMode;
572 }
573 
574 
575 void
576 DrawState::SetPenLocation(BPoint location)
577 {
578 	fPenLocation = location;
579 }
580 
581 
582 BPoint
583 DrawState::PenLocation() const
584 {
585 	return fPenLocation;
586 }
587 
588 
589 void
590 DrawState::SetPenSize(float size)
591 {
592 	fPenSize = size;
593 }
594 
595 
596 //! returns the scaled pen size
597 float
598 DrawState::PenSize() const
599 {
600 	float penSize = fPenSize * fCombinedScale;
601 	// NOTE: As documented in the BeBook,
602 	// pen size is never smaller than 1.0.
603 	// This is supposed to be the smallest
604 	// possible device size.
605 	if (penSize < 1.0)
606 		penSize = 1.0;
607 	return penSize;
608 }
609 
610 
611 //! returns the unscaled pen size
612 float
613 DrawState::UnscaledPenSize() const
614 {
615 	// NOTE: As documented in the BeBook,
616 	// pen size is never smaller than 1.0.
617 	// This is supposed to be the smallest
618 	// possible device size.
619 	return max_c(fPenSize, 1.0);
620 }
621 
622 
623 //! sets the font to be already scaled by fScale
624 void
625 DrawState::SetFont(const ServerFont& font, uint32 flags)
626 {
627 	if (flags == B_FONT_ALL) {
628 		fFont = font;
629 		fUnscaledFontSize = font.Size();
630 		fFont.SetSize(fUnscaledFontSize * fCombinedScale);
631 	} else {
632 		// family & style
633 		if ((flags & B_FONT_FAMILY_AND_STYLE) != 0)
634 			fFont.SetFamilyAndStyle(font.GetFamilyAndStyle());
635 		// size
636 		if ((flags & B_FONT_SIZE) != 0) {
637 			fUnscaledFontSize = font.Size();
638 			fFont.SetSize(fUnscaledFontSize * fCombinedScale);
639 		}
640 		// shear
641 		if ((flags & B_FONT_SHEAR) != 0)
642 			fFont.SetShear(font.Shear());
643 		// rotation
644 		if ((flags & B_FONT_ROTATION) != 0)
645 			fFont.SetRotation(font.Rotation());
646 		// spacing
647 		if ((flags & B_FONT_SPACING) != 0)
648 			fFont.SetSpacing(font.Spacing());
649 		// encoding
650 		if ((flags & B_FONT_ENCODING) != 0)
651 			fFont.SetEncoding(font.Encoding());
652 		// face
653 		if ((flags & B_FONT_FACE) != 0)
654 			fFont.SetFace(font.Face());
655 		// flags
656 		if ((flags & B_FONT_FLAGS) != 0)
657 			fFont.SetFlags(font.Flags());
658 	}
659 }
660 
661 
662 void
663 DrawState::SetForceFontAliasing(bool aliasing)
664 {
665 	fFontAliasing = aliasing;
666 }
667 
668 
669 void
670 DrawState::SetSubPixelPrecise(bool precise)
671 {
672 	fSubPixelPrecise = precise;
673 }
674 
675 
676 void
677 DrawState::SetLineCapMode(cap_mode mode)
678 {
679 	fLineCapMode = mode;
680 }
681 
682 
683 void
684 DrawState::SetLineJoinMode(join_mode mode)
685 {
686 	fLineJoinMode = mode;
687 }
688 
689 
690 void
691 DrawState::SetMiterLimit(float limit)
692 {
693 	fMiterLimit = limit;
694 }
695 
696 
697 void
698 DrawState::SetFillRule(int32 fillRule)
699 {
700 	fFillRule = fillRule;
701 }
702 
703 
704 void
705 DrawState::PrintToStream() const
706 {
707 	printf("\t Origin: (%.1f, %.1f)\n", fOrigin.x, fOrigin.y);
708 	printf("\t Scale: %.2f\n", fScale);
709 	printf("\t Transform: %.2f, %.2f, %.2f, %.2f, %.2f, %.2f\n",
710 		fTransform.sx, fTransform.shy, fTransform.shx,
711 		fTransform.sy, fTransform.tx, fTransform.ty);
712 
713 	printf("\t Pen Location and Size: (%.1f, %.1f) - %.2f (%.2f)\n",
714 		   fPenLocation.x, fPenLocation.y, PenSize(), fPenSize);
715 
716 	printf("\t HighColor: r=%d g=%d b=%d a=%d\n",
717 		fHighColor.red, fHighColor.green, fHighColor.blue, fHighColor.alpha);
718 	printf("\t LowColor: r=%d g=%d b=%d a=%d\n",
719 		fLowColor.red, fLowColor.green, fLowColor.blue, fLowColor.alpha);
720 	printf("\t Pattern: %" B_PRIu64 "\n", fPattern.GetInt64());
721 
722 	printf("\t DrawMode: %" B_PRIu32 "\n", (uint32)fDrawingMode);
723 	printf("\t AlphaSrcMode: %" B_PRId32 "\t AlphaFncMode: %" B_PRId32 "\n",
724 		   (int32)fAlphaSrcMode, (int32)fAlphaFncMode);
725 
726 	printf("\t LineCap: %d\t LineJoin: %d\t MiterLimit: %.2f\n",
727 		   (int16)fLineCapMode, (int16)fLineJoinMode, fMiterLimit);
728 
729 	if (fClippingRegion != NULL)
730 		fClippingRegion->PrintToStream();
731 
732 	printf("\t ===== Font Data =====\n");
733 	printf("\t Style: CURRENTLY NOT SET\n"); // ???
734 	printf("\t Size: %.1f (%.1f)\n", fFont.Size(), fUnscaledFontSize);
735 	printf("\t Shear: %.2f\n", fFont.Shear());
736 	printf("\t Rotation: %.2f\n", fFont.Rotation());
737 	printf("\t Spacing: %" B_PRId32 "\n", fFont.Spacing());
738 	printf("\t Encoding: %" B_PRId32 "\n", fFont.Encoding());
739 	printf("\t Face: %d\n", fFont.Face());
740 	printf("\t Flags: %" B_PRIu32 "\n", fFont.Flags());
741 }
742 
743