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