xref: /haiku/src/servers/app/SimpleTransform.h (revision 4bd0c1066b227cec4b79883bdef697c7a27f2e90)
1 /*
2  * Copyright (c) 2001-2015, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  *		Stephan Aßmus <superstippi@gmx.de>
8  *		Adrien Destugues <pulkomandy@pulkomandy.tk>
9  *		Julian Harnath <julian.harnath@rwth-aachen.de>
10  */
11 
12 #ifndef SIMPLE_TRANSFORM_H
13 #define SIMPLE_TRANSFORM_H
14 
15 #include <GradientLinear.h>
16 #include <GradientRadial.h>
17 #include <GradientRadialFocus.h>
18 #include <GradientDiamond.h>
19 #include <GradientConic.h>
20 #include <Point.h>
21 #include <Region.h>
22 
23 #include "IntPoint.h"
24 #include "IntRect.h"
25 
26 
27 class SimpleTransform {
28 public:
29 	SimpleTransform()
30 		:
31 		fScale(1.0)
32 	{
33 	}
34 
35 	void AddOffset(float x, float y)
36 	{
37 		fOffset.x += x;
38 		fOffset.y += y;
39 	}
40 
41 	void SetScale(float scale)
42 	{
43 		fScale = scale;
44 	}
45 
46 	void Apply(BPoint* point) const
47 	{
48 		_Apply(point->x, point->y);
49 	}
50 
51 	void Apply(IntPoint* point) const
52 	{
53 		_Apply(point->x, point->y);
54 	}
55 
56 	void Apply(BRect* rect) const
57 	{
58 		if (fScale == 1.0) {
59 			rect->OffsetBy(fOffset.x, fOffset.y);
60 		} else {
61 			_Apply(rect->left, rect->top);
62 			_Apply(rect->right, rect->bottom);
63 		}
64 	}
65 
66 	void Apply(IntRect* rect) const
67 	{
68 		if (fScale == 1.0) {
69 			rect->OffsetBy(fOffset.x, fOffset.y);
70 		} else {
71 			_Apply(rect->left, rect->top);
72 			_Apply(rect->right, rect->bottom);
73 		}
74 	}
75 
76 	void Apply(BRegion* region) const
77 	{
78 		if (fScale == 1.0) {
79 			region->OffsetBy(fOffset.x, fOffset.y);
80 		} else {
81 			// TODO: optimize some more
82 			BRegion converted;
83 			int32 count = region->CountRects();
84 			for (int32 i = 0; i < count; i++) {
85 				BRect r = region->RectAt(i);
86 				BPoint lt(r.LeftTop());
87 				BPoint rb(r.RightBottom());
88 				// offset to bottom right corner of pixel before transformation
89 				rb.x++;
90 				rb.y++;
91 				// apply transformation
92 				_Apply(lt.x, lt.y);
93 				_Apply(rb.x, rb.y);
94 				// reset bottom right to pixel "index"
95 				rb.x--;
96 				rb.y--;
97 				// add rect to converted region
98 				// NOTE/TODO: the rect would not have to go
99 				// through the whole intersection test process,
100 				// it is guaranteed not to overlap with any rect
101 				// already contained in the region
102 				converted.Include(BRect(lt, rb));
103 			}
104 			*region = converted;
105 		}
106 	}
107 
108 	void Apply(BGradient* gradient) const
109 	{
110 		switch (gradient->GetType()) {
111 			case BGradient::TYPE_LINEAR:
112 			{
113 				BGradientLinear* linear = (BGradientLinear*) gradient;
114 				BPoint start = linear->Start();
115 				BPoint end = linear->End();
116 				Apply(&start);
117 				Apply(&end);
118 				linear->SetStart(start);
119 				linear->SetEnd(end);
120 				break;
121 			}
122 
123 			case BGradient::TYPE_RADIAL:
124 			{
125 				BGradientRadial* radial = (BGradientRadial*) gradient;
126 				BPoint center = radial->Center();
127 				Apply(&center);
128 				radial->SetCenter(center);
129 				break;
130 			}
131 
132 			case BGradient::TYPE_RADIAL_FOCUS:
133 			{
134 				BGradientRadialFocus* radialFocus =
135 					(BGradientRadialFocus*)gradient;
136 				BPoint center = radialFocus->Center();
137 				BPoint focal = radialFocus->Focal();
138 				Apply(&center);
139 				Apply(&focal);
140 				radialFocus->SetCenter(center);
141 				radialFocus->SetFocal(focal);
142 				break;
143 			}
144 
145 			case BGradient::TYPE_DIAMOND:
146 			{
147 				BGradientDiamond* diamond = (BGradientDiamond*) gradient;
148 				BPoint center = diamond->Center();
149 				Apply(&center);
150 				diamond->SetCenter(center);
151 				break;
152 			}
153 
154 			case BGradient::TYPE_CONIC:
155 			{
156 				BGradientConic* conic = (BGradientConic*) gradient;
157 				BPoint center = conic->Center();
158 				Apply(&center);
159 				conic->SetCenter(center);
160 				break;
161 			}
162 
163 			case BGradient::TYPE_NONE:
164 			{
165 				break;
166 			}
167 		}
168 
169 		// Make sure the gradient is fully padded so that out of bounds access
170 		// get the correct colors
171 		gradient->SortColorStopsByOffset();
172 
173 		BGradient::ColorStop* end = gradient->ColorStopAtFast(
174 			gradient->CountColorStops() - 1);
175 
176 		if (end->offset != 255)
177 			gradient->AddColor(end->color, 255);
178 
179 		BGradient::ColorStop* start = gradient->ColorStopAtFast(0);
180 
181 		if (start->offset != 0)
182 			gradient->AddColor(start->color, 0);
183 
184 		gradient->SortColorStopsByOffset();
185 	}
186 
187 	void Apply(BPoint* destination, const BPoint* source, int32 count) const
188 	{
189 		// TODO: optimize this, it should be smarter
190 		while (count--) {
191 			*destination = *source;
192 			Apply(destination);
193 			source++;
194 			destination++;
195 		}
196 	}
197 
198 	void Apply(BRect* destination, const BRect* source, int32 count) const
199 	{
200 		// TODO: optimize this, it should be smarter
201 		while (count--) {
202 			*destination = *source;
203 			Apply(destination);
204 			source++;
205 			destination++;
206 		}
207 	}
208 
209 	void Apply(BRegion* destination, const BRegion* source, int32 count) const
210 	{
211 		// TODO: optimize this, it should be smarter
212 		while (count--) {
213 			*destination = *source;
214 			Apply(destination);
215 			source++;
216 			destination++;
217 		}
218 	}
219 
220 private:
221 	void _Apply(int32& x, int32& y) const
222 	{
223 		x *= (int32)fScale;
224 		y *= (int32)fScale;
225 		x += (int32)fOffset.x;
226 		y += (int32)fOffset.y;
227 	}
228 
229 	void _Apply(float& x, float& y) const
230 	{
231 		x *= fScale;
232 		y *= fScale;
233 		x += fOffset.x;
234 		y += fOffset.y;
235 	}
236 
237 private:
238 	BPoint	fOffset;
239 	float	fScale;
240 };
241 
242 
243 #endif // SIMPLE_TRANSFORM_H
244