xref: /haiku/src/kits/interface/Gradient.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 <algorithm>
13 #include <math.h>
14 #include <stdio.h>
15 
16 #include <Message.h>
17 
18 
19 // constructor
20 BGradient::ColorStop::ColorStop(const rgb_color c, float o)
21 {
22 	color.red = c.red;
23 	color.green = c.green;
24 	color.blue = c.blue;
25 	color.alpha = c.alpha;
26 	offset = o;
27 }
28 
29 
30 // constructor
31 BGradient::ColorStop::ColorStop(uint8 r, uint8 g, uint8 b, uint8 a, float o)
32 {
33 	color.red = r;
34 	color.green = g;
35 	color.blue = b;
36 	color.alpha = a;
37 	offset = o;
38 }
39 
40 
41 // constructor
42 BGradient::ColorStop::ColorStop(const ColorStop& other)
43 {
44 	color.red = other.color.red;
45 	color.green = other.color.green;
46 	color.blue = other.color.blue;
47 	color.alpha = other.color.alpha;
48 	offset = other.offset;
49 }
50 
51 
52 // constructor
53 BGradient::ColorStop::ColorStop()
54 {
55 	color.red = 0;
56 	color.green = 0;
57 	color.blue = 0;
58 	color.alpha = 255;
59 	offset = 0;
60 }
61 
62 
63 // operator!=
64 bool
65 BGradient::ColorStop::operator!=(const ColorStop& other) const
66 {
67 	return color.red != other.color.red ||
68 	color.green != other.color.green ||
69 	color.blue != other.color.blue ||
70 	color.alpha != other.color.alpha ||
71 	offset != other.offset;
72 }
73 
74 
75 static bool
76 sort_color_stops_by_offset(const BGradient::ColorStop* left,
77 	const BGradient::ColorStop* right)
78 {
79 	return left->offset < right->offset;
80 }
81 
82 
83 // #pragma mark -
84 
85 
86 // constructor
87 BGradient::BGradient()
88 	: BArchivable(),
89 	fColorStops(4),
90 	fType(TYPE_NONE)
91 {
92 }
93 
94 
95 // constructor
96 BGradient::BGradient(BMessage* archive)
97 	: BArchivable(archive),
98 	fColorStops(4),
99 	fType(TYPE_NONE)
100 {
101 	if (!archive)
102 		return;
103 
104 	// color stops
105 	ColorStop stop;
106 	for (int32 i = 0; archive->FindFloat("offset", i, &stop.offset) >= B_OK; i++) {
107 		if (archive->FindInt32("color", i, (int32*)&stop.color) >= B_OK)
108 			AddColorStop(stop, i);
109 		else
110 			break;
111 	}
112 	if (archive->FindInt32("type", (int32*)&fType) < B_OK)
113 		fType = TYPE_LINEAR;
114 
115 	// linear
116 	if (archive->FindFloat("linear_x1", (float*)&fData.linear.x1) < B_OK)
117 		fData.linear.x1 = 0.0f;
118 	if (archive->FindFloat("linear_y1", (float*)&fData.linear.y1) < B_OK)
119 		fData.linear.y1 = 0.0f;
120 	if (archive->FindFloat("linear_x2", (float*)&fData.linear.x2) < B_OK)
121 		fData.linear.x2 = 0.0f;
122 	if (archive->FindFloat("linear_y2", (float*)&fData.linear.y2) < B_OK)
123 		fData.linear.y2 = 0.0f;
124 
125 	// radial
126 	if (archive->FindFloat("radial_cx", (float*)&fData.radial.cx) < B_OK)
127 		fData.radial.cx = 0.0f;
128 	if (archive->FindFloat("radial_cy", (float*)&fData.radial.cy) < B_OK)
129 		fData.radial.cy = 0.0f;
130 	if (archive->FindFloat("radial_radius", (float*)&fData.radial.radius) < B_OK)
131 		fData.radial.radius = 0.0f;
132 
133 	// radial focus
134 	if (archive->FindFloat("radial_f_cx", (float*)&fData.radial_focus.cx) < B_OK)
135 		fData.radial_focus.cx = 0.0f;
136 	if (archive->FindFloat("radial_f_cy", (float*)&fData.radial_focus.cy) < B_OK)
137 		fData.radial_focus.cy = 0.0f;
138 	if (archive->FindFloat("radial_f_fx", (float*)&fData.radial_focus.fx) < B_OK)
139 		fData.radial_focus.fx = 0.0f;
140 	if (archive->FindFloat("radial_f_fy", (float*)&fData.radial_focus.fy) < B_OK)
141 		fData.radial_focus.fy = 0.0f;
142 	if (archive->FindFloat("radial_f_radius", (float*)&fData.radial_focus.radius) < B_OK)
143 		fData.radial_focus.radius = 0.0f;
144 
145 	// diamond
146 	if (archive->FindFloat("diamond_cx", (float*)&fData.diamond.cx) < B_OK)
147 		fData.diamond.cx = 0.0f;
148 	if (archive->FindFloat("diamond_cy", (float*)&fData.diamond.cy) < B_OK)
149 		fData.diamond.cy = 0.0f;
150 
151 	// conic
152 	if (archive->FindFloat("conic_cx", (float*)&fData.conic.cx) < B_OK)
153 		fData.conic.cx = 0.0f;
154 	if (archive->FindFloat("conic_cy", (float*)&fData.conic.cy) < B_OK)
155 		fData.conic.cy = 0.0f;
156 	if (archive->FindFloat("conic_angle", (float*)&fData.conic.angle) < B_OK)
157 		fData.conic.angle = 0.0f;
158 }
159 
160 
161 // destructor
162 BGradient::~BGradient()
163 {
164 	MakeEmpty();
165 }
166 
167 
168 // Archive
169 status_t
170 BGradient::Archive(BMessage* into, bool deep) const
171 {
172 	status_t ret = BArchivable::Archive(into, deep);
173 
174 	// color steps
175 	if (ret >= B_OK) {
176 		for (int32 i = 0; ColorStop* stop = ColorStopAt(i); i++) {
177 			ret = into->AddInt32("color", (const uint32&)stop->color);
178 			if (ret < B_OK)
179 				break;
180 			ret = into->AddFloat("offset", stop->offset);
181 			if (ret < B_OK)
182 				break;
183 		}
184 	}
185 	// gradient type
186 	if (ret >= B_OK)
187 		ret = into->AddInt32("type", (int32)fType);
188 
189 	// linear
190 	if (ret >= B_OK)
191 		ret = into->AddFloat("linear_x1", (float)fData.linear.x1);
192 	if (ret >= B_OK)
193 		ret = into->AddFloat("linear_y1", (float)fData.linear.y1);
194 	if (ret >= B_OK)
195 		ret = into->AddFloat("linear_x2", (float)fData.linear.x2);
196 	if (ret >= B_OK)
197 		ret = into->AddFloat("linear_y2", (float)fData.linear.y2);
198 
199 	// radial
200 	if (ret >= B_OK)
201 		ret = into->AddFloat("radial_cx", (float)fData.radial.cx);
202 	if (ret >= B_OK)
203 		ret = into->AddFloat("radial_cy", (float)fData.radial.cy);
204 	if (ret >= B_OK)
205 		ret = into->AddFloat("radial_radius", (float)fData.radial.radius);
206 
207 	// radial focus
208 	if (ret >= B_OK)
209 		ret = into->AddFloat("radial_f_cx", (float)fData.radial_focus.cx);
210 	if (ret >= B_OK)
211 		ret = into->AddFloat("radial_f_cy", (float)fData.radial_focus.cy);
212 	if (ret >= B_OK)
213 		ret = into->AddFloat("radial_f_fx", (float)fData.radial_focus.fx);
214 	if (ret >= B_OK)
215 		ret = into->AddFloat("radial_f_fy", (float)fData.radial_focus.fy);
216 	if (ret >= B_OK)
217 		ret = into->AddFloat("radial_f_radius", (float)fData.radial_focus.radius);
218 
219 	// diamond
220 	if (ret >= B_OK)
221 		ret = into->AddFloat("diamond_cx", (float)fData.diamond.cx);
222 	if (ret >= B_OK)
223 		ret = into->AddFloat("diamond_cy", (float)fData.diamond.cy);
224 
225 	// conic
226 	if (ret >= B_OK)
227 		ret = into->AddFloat("conic_cx", (float)fData.conic.cx);
228 	if (ret >= B_OK)
229 		ret = into->AddFloat("conic_cy", (float)fData.conic.cy);
230 	if (ret >= B_OK)
231 		ret = into->AddFloat("conic_angle", (float)fData.conic.angle);
232 
233 	// finish off
234 	if (ret >= B_OK)
235 		ret = into->AddString("class", "BGradient");
236 
237 	return ret;
238 }
239 
240 
241 // operator=
242 BGradient&
243 BGradient::operator=(const BGradient& other)
244 {
245 	SetColorStops(other);
246 	fType = other.fType;
247 	return *this;
248 }
249 
250 
251 // operator==
252 bool
253 BGradient::operator==(const BGradient& other) const
254 {
255 	return ((other.GetType() == GetType()) && ColorStopsAreEqual(other));
256 }
257 
258 
259 // operator!=
260 bool
261 BGradient::operator!=(const BGradient& other) const
262 {
263 	return !(*this == other);
264 }
265 
266 
267 // ColorStopsAreEqual
268 bool
269 BGradient::ColorStopsAreEqual(const BGradient& other) const
270 {
271 	int32 count = CountColorStops();
272 	if (count == other.CountColorStops() &&
273 		fType == other.fType) {
274 
275 		bool equal = true;
276 		for (int32 i = 0; i < count; i++) {
277 			ColorStop* ourStop = ColorStopAtFast(i);
278 			ColorStop* otherStop = other.ColorStopAtFast(i);
279 			if (*ourStop != *otherStop) {
280 				equal = false;
281 				break;
282 			}
283 		}
284 		return equal;
285 	}
286 	return false;
287 }
288 
289 
290 // SetColorStops
291 void
292 BGradient::SetColorStops(const BGradient& other)
293 {
294 	MakeEmpty();
295 	for (int32 i = 0; ColorStop* stop = other.ColorStopAt(i); i++)
296 		AddColorStop(*stop, i);
297 }
298 
299 
300 // AddColor
301 int32
302 BGradient::AddColor(const rgb_color& color, float offset)
303 {
304 	// Out of bounds stops would crash the app_server
305 	if (offset < 0.f || offset > 255.f)
306 		return -1;
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 	// Use stable sort: stops with the same offset will retain their original
432 	// order. This can be used to have sharp color changes in the gradient.
433 	// BList.SortItems() uses qsort(), which isn't stable, and sometimes swaps
434 	// such stops.
435 	const BGradient::ColorStop** first = (const BGradient::ColorStop**)fColorStops.Items();
436 	const BGradient::ColorStop** last = first + fColorStops.CountItems();
437 	std::stable_sort(first, last, sort_color_stops_by_offset);
438 }
439 
440 
441 // MakeEmpty
442 void
443 BGradient::MakeEmpty()
444 {
445 	int32 count = CountColorStops();
446 	for (int32 i = 0; i < count; i++)
447 		delete ColorStopAtFast(i);
448 	fColorStops.MakeEmpty();
449 }
450