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