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