xref: /haiku/src/kits/interface/Gradient.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /*
2  * Copyright 2006-2009, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  *		Artur Wyszynski <harakash@gmail.com>
8  */
9 
10 #include "Gradient.h"
11 
12 #include <math.h>
13 #include <stdio.h>
14 
15 #include <Message.h>
16 
17 
18 // constructor
19 BGradient::ColorStop::ColorStop(const rgb_color c, float o)
20 {
21 	color.red = c.red;
22 	color.green = c.green;
23 	color.blue = c.blue;
24 	color.alpha = c.alpha;
25 	offset = o;
26 }
27 
28 
29 // constructor
30 BGradient::ColorStop::ColorStop(uint8 r, uint8 g, uint8 b, uint8 a, float o)
31 {
32 	color.red = r;
33 	color.green = g;
34 	color.blue = b;
35 	color.alpha = a;
36 	offset = o;
37 }
38 
39 
40 // constructor
41 BGradient::ColorStop::ColorStop(const ColorStop& other)
42 {
43 	color.red = other.color.red;
44 	color.green = other.color.green;
45 	color.blue = other.color.blue;
46 	color.alpha = other.color.alpha;
47 	offset = other.offset;
48 }
49 
50 
51 // constructor
52 BGradient::ColorStop::ColorStop()
53 {
54 	color.red = 0;
55 	color.green = 0;
56 	color.blue = 0;
57 	color.alpha = 255;
58 	offset = 0;
59 }
60 
61 
62 // operator!=
63 bool
64 BGradient::ColorStop::operator!=(const ColorStop& other) const
65 {
66 	return color.red != other.color.red ||
67 	color.green != other.color.green ||
68 	color.blue != other.color.blue ||
69 	color.alpha != other.color.alpha ||
70 	offset != other.offset;
71 }
72 
73 
74 static int
75 sort_color_stops_by_offset(const void* _left, const void* _right)
76 {
77 	const BGradient::ColorStop** left = (const BGradient::ColorStop**)_left;
78 	const BGradient::ColorStop** right = (const BGradient::ColorStop**)_right;
79 	if ((*left)->offset > (*right)->offset)
80 		return 1;
81 	else if ((*left)->offset < (*right)->offset)
82 		return -1;
83 	return 0;
84 }
85 
86 
87 // #pragma mark -
88 
89 
90 // constructor
91 BGradient::BGradient()
92 	: BArchivable(),
93 	fColorStops(4),
94 	fType(TYPE_NONE)
95 {
96 }
97 
98 
99 // constructor
100 BGradient::BGradient(BMessage* archive)
101 	: BArchivable(archive),
102 	fColorStops(4),
103 	fType(TYPE_NONE)
104 {
105 	if (!archive)
106 		return;
107 
108 	// color stops
109 	ColorStop stop;
110 	for (int32 i = 0; archive->FindFloat("offset", i, &stop.offset) >= B_OK; i++) {
111 		if (archive->FindInt32("color", i, (int32*)&stop.color) >= B_OK)
112 			AddColorStop(stop, i);
113 		else
114 			break;
115 	}
116 	if (archive->FindInt32("type", (int32*)&fType) < B_OK)
117 		fType = TYPE_LINEAR;
118 
119 	// linear
120 	if (archive->FindFloat("linear_x1", (float*)&fData.linear.x1) < B_OK)
121 		fData.linear.x1 = 0.0f;
122 	if (archive->FindFloat("linear_y1", (float*)&fData.linear.y1) < B_OK)
123 		fData.linear.y1 = 0.0f;
124 	if (archive->FindFloat("linear_x2", (float*)&fData.linear.x2) < B_OK)
125 		fData.linear.x2 = 0.0f;
126 	if (archive->FindFloat("linear_y2", (float*)&fData.linear.y2) < B_OK)
127 		fData.linear.y2 = 0.0f;
128 
129 	// radial
130 	if (archive->FindFloat("radial_cx", (float*)&fData.radial.cx) < B_OK)
131 		fData.radial.cx = 0.0f;
132 	if (archive->FindFloat("radial_cy", (float*)&fData.radial.cy) < B_OK)
133 		fData.radial.cy = 0.0f;
134 	if (archive->FindFloat("radial_radius", (float*)&fData.radial.radius) < B_OK)
135 		fData.radial.radius = 0.0f;
136 
137 	// radial focus
138 	if (archive->FindFloat("radial_f_cx", (float*)&fData.radial_focus.cx) < B_OK)
139 		fData.radial_focus.cx = 0.0f;
140 	if (archive->FindFloat("radial_f_cy", (float*)&fData.radial_focus.cy) < B_OK)
141 		fData.radial_focus.cy = 0.0f;
142 	if (archive->FindFloat("radial_f_fx", (float*)&fData.radial_focus.fx) < B_OK)
143 		fData.radial_focus.fx = 0.0f;
144 	if (archive->FindFloat("radial_f_fy", (float*)&fData.radial_focus.fy) < B_OK)
145 		fData.radial_focus.fy = 0.0f;
146 	if (archive->FindFloat("radial_f_radius", (float*)&fData.radial_focus.radius) < B_OK)
147 		fData.radial_focus.radius = 0.0f;
148 
149 	// diamond
150 	if (archive->FindFloat("diamond_cx", (float*)&fData.diamond.cx) < B_OK)
151 		fData.diamond.cx = 0.0f;
152 	if (archive->FindFloat("diamond_cy", (float*)&fData.diamond.cy) < B_OK)
153 		fData.diamond.cy = 0.0f;
154 
155 	// conic
156 	if (archive->FindFloat("conic_cx", (float*)&fData.conic.cx) < B_OK)
157 		fData.conic.cx = 0.0f;
158 	if (archive->FindFloat("conic_cy", (float*)&fData.conic.cy) < B_OK)
159 		fData.conic.cy = 0.0f;
160 	if (archive->FindFloat("conic_angle", (float*)&fData.conic.angle) < B_OK)
161 		fData.conic.angle = 0.0f;
162 }
163 
164 
165 // destructor
166 BGradient::~BGradient()
167 {
168 	MakeEmpty();
169 }
170 
171 
172 // Archive
173 status_t
174 BGradient::Archive(BMessage* into, bool deep) const
175 {
176 	status_t ret = BArchivable::Archive(into, deep);
177 
178 	// color steps
179 	if (ret >= B_OK) {
180 		for (int32 i = 0; ColorStop* stop = ColorStopAt(i); i++) {
181 			ret = into->AddInt32("color", (const uint32&)stop->color);
182 			if (ret < B_OK)
183 				break;
184 			ret = into->AddFloat("offset", stop->offset);
185 			if (ret < B_OK)
186 				break;
187 		}
188 	}
189 	// gradient type
190 	if (ret >= B_OK)
191 		ret = into->AddInt32("type", (int32)fType);
192 
193 	// linear
194 	if (ret >= B_OK)
195 		ret = into->AddFloat("linear_x1", (float)fData.linear.x1);
196 	if (ret >= B_OK)
197 		ret = into->AddFloat("linear_y1", (float)fData.linear.y1);
198 	if (ret >= B_OK)
199 		ret = into->AddFloat("linear_x2", (float)fData.linear.x2);
200 	if (ret >= B_OK)
201 		ret = into->AddFloat("linear_y2", (float)fData.linear.y2);
202 
203 	// radial
204 	if (ret >= B_OK)
205 		ret = into->AddFloat("radial_cx", (float)fData.radial.cx);
206 	if (ret >= B_OK)
207 		ret = into->AddFloat("radial_cy", (float)fData.radial.cy);
208 	if (ret >= B_OK)
209 		ret = into->AddFloat("radial_radius", (float)fData.radial.radius);
210 
211 	// radial focus
212 	if (ret >= B_OK)
213 		ret = into->AddFloat("radial_f_cx", (float)fData.radial_focus.cx);
214 	if (ret >= B_OK)
215 		ret = into->AddFloat("radial_f_cy", (float)fData.radial_focus.cy);
216 	if (ret >= B_OK)
217 		ret = into->AddFloat("radial_f_fx", (float)fData.radial_focus.fx);
218 	if (ret >= B_OK)
219 		ret = into->AddFloat("radial_f_fy", (float)fData.radial_focus.fy);
220 	if (ret >= B_OK)
221 		ret = into->AddFloat("radial_f_radius", (float)fData.radial_focus.radius);
222 
223 	// diamond
224 	if (ret >= B_OK)
225 		ret = into->AddFloat("diamond_cx", (float)fData.diamond.cx);
226 	if (ret >= B_OK)
227 		ret = into->AddFloat("diamond_cy", (float)fData.diamond.cy);
228 
229 	// conic
230 	if (ret >= B_OK)
231 		ret = into->AddFloat("conic_cx", (float)fData.conic.cx);
232 	if (ret >= B_OK)
233 		ret = into->AddFloat("conic_cy", (float)fData.conic.cy);
234 	if (ret >= B_OK)
235 		ret = into->AddFloat("conic_angle", (float)fData.conic.angle);
236 
237 	// finish off
238 	if (ret >= B_OK)
239 		ret = into->AddString("class", "BGradient");
240 
241 	return ret;
242 }
243 
244 
245 // operator=
246 BGradient&
247 BGradient::operator=(const BGradient& other)
248 {
249 	SetColorStops(other);
250 	fType = other.fType;
251 	return *this;
252 }
253 
254 
255 // operator==
256 bool
257 BGradient::operator==(const BGradient& other) const
258 {
259 	return ((other.GetType() == GetType()) && ColorStopsAreEqual(other));
260 }
261 
262 
263 // operator!=
264 bool
265 BGradient::operator!=(const BGradient& other) const
266 {
267 	return !(*this == other);
268 }
269 
270 
271 // ColorStopsAreEqual
272 bool
273 BGradient::ColorStopsAreEqual(const BGradient& other) const
274 {
275 	int32 count = CountColorStops();
276 	if (count == other.CountColorStops() &&
277 		fType == other.fType) {
278 
279 		bool equal = true;
280 		for (int32 i = 0; i < count; i++) {
281 			ColorStop* ourStop = ColorStopAtFast(i);
282 			ColorStop* otherStop = other.ColorStopAtFast(i);
283 			if (*ourStop != *otherStop) {
284 				equal = false;
285 				break;
286 			}
287 		}
288 		return equal;
289 	}
290 	return false;
291 }
292 
293 
294 // SetColorStops
295 void
296 BGradient::SetColorStops(const BGradient& other)
297 {
298 	MakeEmpty();
299 	for (int32 i = 0; ColorStop* stop = other.ColorStopAt(i); i++)
300 		AddColorStop(*stop, i);
301 }
302 
303 
304 // AddColor
305 int32
306 BGradient::AddColor(const rgb_color& color, float offset)
307 {
308 	// find the correct index (sorted by offset)
309 	ColorStop* stop = new ColorStop(color, offset);
310 	int32 index = 0;
311 	int32 count = CountColorStops();
312 	for (; index < count; index++) {
313 		ColorStop* s = ColorStopAtFast(index);
314 		if (s->offset > stop->offset)
315 			break;
316 	}
317 	if (!fColorStops.AddItem((void*)stop, index)) {
318 		delete stop;
319 		return -1;
320 	}
321 	return index;
322 }
323 
324 
325 // AddColorStop
326 bool
327 BGradient::AddColorStop(const ColorStop& colorStop, int32 index)
328 {
329 	ColorStop* stop = new ColorStop(colorStop);
330 	if (!fColorStops.AddItem((void*)stop, index)) {
331 		delete stop;
332 		return false;
333 	}
334 	return true;
335 }
336 
337 
338 // RemoveColor
339 bool
340 BGradient::RemoveColor(int32 index)
341 {
342 	ColorStop* stop = (ColorStop*)fColorStops.RemoveItem(index);
343 	if (!stop) {
344 		return false;
345 	}
346 	delete stop;
347 	return true;
348 }
349 
350 
351 // SetColorStop
352 bool
353 BGradient::SetColorStop(int32 index, const ColorStop& color)
354 {
355 	if (ColorStop* stop = ColorStopAt(index)) {
356 		if (*stop != color) {
357 			stop->color = color.color;
358 			stop->offset = color.offset;
359 			return true;
360 		}
361 	}
362 	return false;
363 }
364 
365 
366 // SetColor
367 bool
368 BGradient::SetColor(int32 index, const rgb_color& color)
369 {
370 	ColorStop* stop = ColorStopAt(index);
371 	if (stop && stop->color != color) {
372 		stop->color = color;
373 		return true;
374 	}
375 	return false;
376 }
377 
378 
379 // SetOffset
380 bool
381 BGradient::SetOffset(int32 index, float offset)
382 {
383 	ColorStop* stop = ColorStopAt(index);
384 	if (stop && stop->offset != offset) {
385 		stop->offset = offset;
386 		return true;
387 	}
388 	return false;
389 }
390 
391 
392 // CountColorStops
393 int32
394 BGradient::CountColorStops() const
395 {
396 	return fColorStops.CountItems();
397 }
398 
399 
400 // ColorStopAt
401 BGradient::ColorStop*
402 BGradient::ColorStopAt(int32 index) const
403 {
404 	return (ColorStop*)fColorStops.ItemAt(index);
405 }
406 
407 
408 // ColorStopAtFast
409 BGradient::ColorStop*
410 BGradient::ColorStopAtFast(int32 index) const
411 {
412 	return (ColorStop*)fColorStops.ItemAtFast(index);
413 }
414 
415 
416 // ColorStops
417 BGradient::ColorStop*
418 BGradient::ColorStops() const
419 {
420 	if (CountColorStops() > 0) {
421 		return (ColorStop*) fColorStops.Items();
422 	}
423 	return NULL;
424 }
425 
426 
427 // SortColorStopsByOffset
428 void
429 BGradient::SortColorStopsByOffset()
430 {
431 	fColorStops.SortItems(sort_color_stops_by_offset);
432 }
433 
434 
435 // MakeEmpty
436 void
437 BGradient::MakeEmpty()
438 {
439 	int32 count = CountColorStops();
440 	for (int32 i = 0; i < count; i++)
441 		delete ColorStopAtFast(i);
442 	fColorStops.MakeEmpty();
443 }
444