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
27class SimpleTransform {
28public:
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
220private:
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
237private:
238	BPoint	fOffset;
239	float	fScale;
240};
241
242
243#endif // SIMPLE_TRANSFORM_H
244