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
27using std::nothrow;
28
29
30DrawState::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
59DrawState::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
95DrawState::~DrawState()
96{
97	delete fClippingRegion;
98	delete fPreviousState;
99}
100
101
102DrawState*
103DrawState::PushState()
104{
105	DrawState* next = new (nothrow) DrawState(this);
106	return next;
107}
108
109
110DrawState*
111DrawState::PopState()
112{
113	DrawState* previous = PreviousState();
114
115	fPreviousState = NULL;
116	delete this;
117
118	return previous;
119}
120
121
122void
123DrawState::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
184void
185DrawState::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
236void
237DrawState::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
285void
286DrawState::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
303void
304DrawState::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
324void
325DrawState::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
339bool
340DrawState::HasClipping() const
341{
342	if (fClippingRegion)
343		return true;
344	if (fPreviousState)
345		return fPreviousState->HasClipping();
346	return false;
347}
348
349
350bool
351DrawState::HasAdditionalClipping() const
352{
353	return fClippingRegion != NULL;
354}
355
356
357bool
358DrawState::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
379void
380DrawState::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
392void
393DrawState::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
404void
405DrawState::Transform(BPoint* point) const
406{
407	Transform(&(point->x), &(point->y));
408}
409
410
411void
412DrawState::Transform(BRect* rect) const
413{
414	Transform(&(rect->left), &(rect->top));
415	Transform(&(rect->right), &(rect->bottom));
416}
417
418
419void
420DrawState::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
453void
454DrawState::InverseTransform(BPoint* point) const
455{
456	InverseTransform(&(point->x), &(point->y));
457}
458
459
460// #pragma mark -
461
462
463void
464DrawState::SetHighColor(const rgb_color& color)
465{
466	fHighColor = color;
467}
468
469
470void
471DrawState::SetLowColor(const rgb_color& color)
472{
473	fLowColor = color;
474}
475
476
477void
478DrawState::SetPattern(const Pattern& pattern)
479{
480	fPattern = pattern;
481}
482
483
484void
485DrawState::SetDrawingMode(drawing_mode mode)
486{
487	fDrawingMode = mode;
488}
489
490
491void
492DrawState::SetBlendingMode(source_alpha srcMode, alpha_function fncMode)
493{
494	fAlphaSrcMode = srcMode;
495	fAlphaFncMode = fncMode;
496}
497
498
499void
500DrawState::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
509const BPoint&
510DrawState::PenLocation() const
511{
512	// TODO: See above
513	return fPenLocation;
514}
515
516
517void
518DrawState::SetPenSize(float size)
519{
520	fPenSize = size;
521}
522
523
524//! returns the scaled pen size
525float
526DrawState::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
540float
541DrawState::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
552void
553DrawState::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
590void
591DrawState::SetForceFontAliasing(bool aliasing)
592{
593	fFontAliasing = aliasing;
594}
595
596
597void
598DrawState::SetSubPixelPrecise(bool precise)
599{
600	fSubPixelPrecise = precise;
601}
602
603
604void
605DrawState::SetLineCapMode(cap_mode mode)
606{
607	fLineCapMode = mode;
608}
609
610
611void
612DrawState::SetLineJoinMode(join_mode mode)
613{
614	fLineJoinMode = mode;
615}
616
617
618void
619DrawState::SetMiterLimit(float limit)
620{
621	fMiterLimit = limit;
622}
623
624
625void
626DrawState::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: %" B_PRIu64 "\n", fPattern.GetInt64());
639
640	printf("\t DrawMode: %" B_PRIu32 "\n", (uint32)fDrawingMode);
641	printf("\t AlphaSrcMode: %" B_PRId32 "\t AlphaFncMode: %" B_PRId32 "\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: %" B_PRId32 "\n", fFont.Spacing());
656	printf("\t Encoding: %" B_PRId32 "\n", fFont.Encoding());
657	printf("\t Face: %d\n", fFont.Face());
658	printf("\t Flags: %" B_PRIu32 "\n", fFont.Flags());
659}
660
661