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 <DataIO.h>
17 #include <Message.h>
18
19 #include <AutoDeleter.h>
20 #include <GradientLinear.h>
21 #include <GradientRadial.h>
22 #include <GradientRadialFocus.h>
23 #include <GradientDiamond.h>
24 #include <GradientConic.h>
25
26
27 // constructor
ColorStop(const rgb_color c,float o)28 BGradient::ColorStop::ColorStop(const rgb_color c, float o)
29 {
30 color.red = c.red;
31 color.green = c.green;
32 color.blue = c.blue;
33 color.alpha = c.alpha;
34 offset = o;
35 }
36
37
38 // constructor
ColorStop(uint8 r,uint8 g,uint8 b,uint8 a,float o)39 BGradient::ColorStop::ColorStop(uint8 r, uint8 g, uint8 b, uint8 a, float o)
40 {
41 color.red = r;
42 color.green = g;
43 color.blue = b;
44 color.alpha = a;
45 offset = o;
46 }
47
48
49 // constructor
ColorStop(const ColorStop & other)50 BGradient::ColorStop::ColorStop(const ColorStop& other)
51 {
52 color.red = other.color.red;
53 color.green = other.color.green;
54 color.blue = other.color.blue;
55 color.alpha = other.color.alpha;
56 offset = other.offset;
57 }
58
59
60 // constructor
ColorStop()61 BGradient::ColorStop::ColorStop()
62 {
63 color.red = 0;
64 color.green = 0;
65 color.blue = 0;
66 color.alpha = 255;
67 offset = 0;
68 }
69
70
71 // operator!=
72 bool
operator !=(const ColorStop & other) const73 BGradient::ColorStop::operator!=(const ColorStop& other) const
74 {
75 return color.red != other.color.red ||
76 color.green != other.color.green ||
77 color.blue != other.color.blue ||
78 color.alpha != other.color.alpha ||
79 offset != other.offset;
80 }
81
82
83 static bool
sort_color_stops_by_offset(const BGradient::ColorStop * left,const BGradient::ColorStop * right)84 sort_color_stops_by_offset(const BGradient::ColorStop* left,
85 const BGradient::ColorStop* right)
86 {
87 return left->offset < right->offset;
88 }
89
90
91 // #pragma mark -
92
93
94 // constructor
BGradient()95 BGradient::BGradient()
96 : BArchivable(),
97 fColorStops(4),
98 fType(TYPE_NONE)
99 {
100 }
101
102
BGradient(const BGradient & other)103 BGradient::BGradient(const BGradient& other)
104 : BArchivable(),
105 fColorStops(std::max((int32)4, other.CountColorStops()))
106 {
107 *this = other;
108 }
109
110
111 // constructor
BGradient(BMessage * archive)112 BGradient::BGradient(BMessage* archive)
113 : BArchivable(archive),
114 fColorStops(4),
115 fType(TYPE_NONE)
116 {
117 if (!archive)
118 return;
119
120 // color stops
121 ColorStop stop;
122 for (int32 i = 0; archive->FindFloat("offset", i, &stop.offset) >= B_OK; i++) {
123 if (archive->FindInt32("color", i, (int32*)&stop.color) >= B_OK)
124 AddColorStop(stop, i);
125 else
126 break;
127 }
128 if (archive->FindInt32("type", (int32*)&fType) < B_OK)
129 fType = TYPE_LINEAR;
130
131 // linear
132 if (archive->FindFloat("linear_x1", (float*)&fData.linear.x1) < B_OK)
133 fData.linear.x1 = 0.0f;
134 if (archive->FindFloat("linear_y1", (float*)&fData.linear.y1) < B_OK)
135 fData.linear.y1 = 0.0f;
136 if (archive->FindFloat("linear_x2", (float*)&fData.linear.x2) < B_OK)
137 fData.linear.x2 = 0.0f;
138 if (archive->FindFloat("linear_y2", (float*)&fData.linear.y2) < B_OK)
139 fData.linear.y2 = 0.0f;
140
141 // radial
142 if (archive->FindFloat("radial_cx", (float*)&fData.radial.cx) < B_OK)
143 fData.radial.cx = 0.0f;
144 if (archive->FindFloat("radial_cy", (float*)&fData.radial.cy) < B_OK)
145 fData.radial.cy = 0.0f;
146 if (archive->FindFloat("radial_radius", (float*)&fData.radial.radius) < B_OK)
147 fData.radial.radius = 0.0f;
148
149 // radial focus
150 if (archive->FindFloat("radial_f_cx", (float*)&fData.radial_focus.cx) < B_OK)
151 fData.radial_focus.cx = 0.0f;
152 if (archive->FindFloat("radial_f_cy", (float*)&fData.radial_focus.cy) < B_OK)
153 fData.radial_focus.cy = 0.0f;
154 if (archive->FindFloat("radial_f_fx", (float*)&fData.radial_focus.fx) < B_OK)
155 fData.radial_focus.fx = 0.0f;
156 if (archive->FindFloat("radial_f_fy", (float*)&fData.radial_focus.fy) < B_OK)
157 fData.radial_focus.fy = 0.0f;
158 if (archive->FindFloat("radial_f_radius", (float*)&fData.radial_focus.radius) < B_OK)
159 fData.radial_focus.radius = 0.0f;
160
161 // diamond
162 if (archive->FindFloat("diamond_cx", (float*)&fData.diamond.cx) < B_OK)
163 fData.diamond.cx = 0.0f;
164 if (archive->FindFloat("diamond_cy", (float*)&fData.diamond.cy) < B_OK)
165 fData.diamond.cy = 0.0f;
166
167 // conic
168 if (archive->FindFloat("conic_cx", (float*)&fData.conic.cx) < B_OK)
169 fData.conic.cx = 0.0f;
170 if (archive->FindFloat("conic_cy", (float*)&fData.conic.cy) < B_OK)
171 fData.conic.cy = 0.0f;
172 if (archive->FindFloat("conic_angle", (float*)&fData.conic.angle) < B_OK)
173 fData.conic.angle = 0.0f;
174 }
175
176
177 // destructor
~BGradient()178 BGradient::~BGradient()
179 {
180 MakeEmpty();
181 }
182
183
184 // Archive
185 status_t
Archive(BMessage * into,bool deep) const186 BGradient::Archive(BMessage* into, bool deep) const
187 {
188 status_t ret = BArchivable::Archive(into, deep);
189
190 // color steps
191 if (ret >= B_OK) {
192 for (int32 i = 0; ColorStop* stop = ColorStopAt(i); i++) {
193 ret = into->AddInt32("color", (const uint32&)stop->color);
194 if (ret < B_OK)
195 break;
196 ret = into->AddFloat("offset", stop->offset);
197 if (ret < B_OK)
198 break;
199 }
200 }
201 // gradient type
202 if (ret >= B_OK)
203 ret = into->AddInt32("type", (int32)fType);
204
205 // linear
206 if (ret >= B_OK)
207 ret = into->AddFloat("linear_x1", (float)fData.linear.x1);
208 if (ret >= B_OK)
209 ret = into->AddFloat("linear_y1", (float)fData.linear.y1);
210 if (ret >= B_OK)
211 ret = into->AddFloat("linear_x2", (float)fData.linear.x2);
212 if (ret >= B_OK)
213 ret = into->AddFloat("linear_y2", (float)fData.linear.y2);
214
215 // radial
216 if (ret >= B_OK)
217 ret = into->AddFloat("radial_cx", (float)fData.radial.cx);
218 if (ret >= B_OK)
219 ret = into->AddFloat("radial_cy", (float)fData.radial.cy);
220 if (ret >= B_OK)
221 ret = into->AddFloat("radial_radius", (float)fData.radial.radius);
222
223 // radial focus
224 if (ret >= B_OK)
225 ret = into->AddFloat("radial_f_cx", (float)fData.radial_focus.cx);
226 if (ret >= B_OK)
227 ret = into->AddFloat("radial_f_cy", (float)fData.radial_focus.cy);
228 if (ret >= B_OK)
229 ret = into->AddFloat("radial_f_fx", (float)fData.radial_focus.fx);
230 if (ret >= B_OK)
231 ret = into->AddFloat("radial_f_fy", (float)fData.radial_focus.fy);
232 if (ret >= B_OK)
233 ret = into->AddFloat("radial_f_radius", (float)fData.radial_focus.radius);
234
235 // diamond
236 if (ret >= B_OK)
237 ret = into->AddFloat("diamond_cx", (float)fData.diamond.cx);
238 if (ret >= B_OK)
239 ret = into->AddFloat("diamond_cy", (float)fData.diamond.cy);
240
241 // conic
242 if (ret >= B_OK)
243 ret = into->AddFloat("conic_cx", (float)fData.conic.cx);
244 if (ret >= B_OK)
245 ret = into->AddFloat("conic_cy", (float)fData.conic.cy);
246 if (ret >= B_OK)
247 ret = into->AddFloat("conic_angle", (float)fData.conic.angle);
248
249 // finish off
250 if (ret >= B_OK)
251 ret = into->AddString("class", "BGradient");
252
253 return ret;
254 }
255
256
257 // operator=
258 BGradient&
operator =(const BGradient & other)259 BGradient::operator=(const BGradient& other)
260 {
261 if (&other == this)
262 return *this;
263
264 SetColorStops(other);
265 fType = other.fType;
266 switch (fType) {
267 case TYPE_LINEAR:
268 fData.linear = other.fData.linear;
269 break;
270 case TYPE_RADIAL:
271 fData.radial = other.fData.radial;
272 break;
273 case TYPE_RADIAL_FOCUS:
274 fData.radial_focus = other.fData.radial_focus;
275 break;
276 case TYPE_DIAMOND:
277 fData.diamond = other.fData.diamond;
278 break;
279 case TYPE_CONIC:
280 fData.conic = other.fData.conic;
281 break;
282 case TYPE_NONE:
283 break;
284 }
285 return *this;
286 }
287
288
289 // operator==
290 bool
operator ==(const BGradient & other) const291 BGradient::operator==(const BGradient& other) const
292 {
293 return ((other.GetType() == GetType()) && ColorStopsAreEqual(other));
294 }
295
296
297 // operator!=
298 bool
operator !=(const BGradient & other) const299 BGradient::operator!=(const BGradient& other) const
300 {
301 return !(*this == other);
302 }
303
304
305 // ColorStopsAreEqual
306 bool
ColorStopsAreEqual(const BGradient & other) const307 BGradient::ColorStopsAreEqual(const BGradient& other) const
308 {
309 int32 count = CountColorStops();
310 if (count == other.CountColorStops() &&
311 fType == other.fType) {
312
313 bool equal = true;
314 for (int32 i = 0; i < count; i++) {
315 ColorStop* ourStop = ColorStopAtFast(i);
316 ColorStop* otherStop = other.ColorStopAtFast(i);
317 if (*ourStop != *otherStop) {
318 equal = false;
319 break;
320 }
321 }
322 return equal;
323 }
324 return false;
325 }
326
327
328 // SetColorStops
329 void
SetColorStops(const BGradient & other)330 BGradient::SetColorStops(const BGradient& other)
331 {
332 MakeEmpty();
333 for (int32 i = 0; ColorStop* stop = other.ColorStopAt(i); i++)
334 AddColorStop(*stop, i);
335 }
336
337
338 // AddColor
339 int32
AddColor(const rgb_color & color,float offset)340 BGradient::AddColor(const rgb_color& color, float offset)
341 {
342 // Out of bounds stops would crash the app_server
343 if (offset < 0.f || offset > 255.f)
344 return -1;
345
346 // find the correct index (sorted by offset)
347 ColorStop* stop = new ColorStop(color, offset);
348 int32 index = 0;
349 int32 count = CountColorStops();
350 for (; index < count; index++) {
351 ColorStop* s = ColorStopAtFast(index);
352 if (s->offset > stop->offset)
353 break;
354 }
355 if (!fColorStops.AddItem((void*)stop, index)) {
356 delete stop;
357 return -1;
358 }
359 return index;
360 }
361
362
363 // AddColorStop
364 bool
AddColorStop(const ColorStop & colorStop,int32 index)365 BGradient::AddColorStop(const ColorStop& colorStop, int32 index)
366 {
367 ColorStop* stop = new ColorStop(colorStop);
368 if (!fColorStops.AddItem((void*)stop, index)) {
369 delete stop;
370 return false;
371 }
372 return true;
373 }
374
375
376 // RemoveColor
377 bool
RemoveColor(int32 index)378 BGradient::RemoveColor(int32 index)
379 {
380 ColorStop* stop = (ColorStop*)fColorStops.RemoveItem(index);
381 if (!stop) {
382 return false;
383 }
384 delete stop;
385 return true;
386 }
387
388
389 // SetColorStop
390 bool
SetColorStop(int32 index,const ColorStop & color)391 BGradient::SetColorStop(int32 index, const ColorStop& color)
392 {
393 if (ColorStop* stop = ColorStopAt(index)) {
394 if (*stop != color) {
395 stop->color = color.color;
396 stop->offset = color.offset;
397 return true;
398 }
399 }
400 return false;
401 }
402
403
404 // SetColor
405 bool
SetColor(int32 index,const rgb_color & color)406 BGradient::SetColor(int32 index, const rgb_color& color)
407 {
408 ColorStop* stop = ColorStopAt(index);
409 if (stop && stop->color != color) {
410 stop->color = color;
411 return true;
412 }
413 return false;
414 }
415
416
417 // SetOffset
418 bool
SetOffset(int32 index,float offset)419 BGradient::SetOffset(int32 index, float offset)
420 {
421 ColorStop* stop = ColorStopAt(index);
422 if (stop && stop->offset != offset) {
423 stop->offset = offset;
424 return true;
425 }
426 return false;
427 }
428
429
430 // CountColorStops
431 int32
CountColorStops() const432 BGradient::CountColorStops() const
433 {
434 return fColorStops.CountItems();
435 }
436
437
438 // ColorStopAt
439 BGradient::ColorStop*
ColorStopAt(int32 index) const440 BGradient::ColorStopAt(int32 index) const
441 {
442 return (ColorStop*)fColorStops.ItemAt(index);
443 }
444
445
446 // ColorStopAtFast
447 BGradient::ColorStop*
ColorStopAtFast(int32 index) const448 BGradient::ColorStopAtFast(int32 index) const
449 {
450 return (ColorStop*)fColorStops.ItemAtFast(index);
451 }
452
453
454 // ColorStops
455 BGradient::ColorStop*
ColorStops() const456 BGradient::ColorStops() const
457 {
458 if (CountColorStops() > 0) {
459 return (ColorStop*) fColorStops.Items();
460 }
461 return NULL;
462 }
463
464
465 // SortColorStopsByOffset
466 void
SortColorStopsByOffset()467 BGradient::SortColorStopsByOffset()
468 {
469 // Use stable sort: stops with the same offset will retain their original
470 // order. This can be used to have sharp color changes in the gradient.
471 // BList.SortItems() uses qsort(), which isn't stable, and sometimes swaps
472 // such stops.
473 const BGradient::ColorStop** first = (const BGradient::ColorStop**)fColorStops.Items();
474 const BGradient::ColorStop** last = first + fColorStops.CountItems();
475 std::stable_sort(first, last, sort_color_stops_by_offset);
476 }
477
478
479 // MakeEmpty
480 void
MakeEmpty()481 BGradient::MakeEmpty()
482 {
483 int32 count = CountColorStops();
484 for (int32 i = 0; i < count; i++)
485 delete ColorStopAtFast(i);
486 fColorStops.MakeEmpty();
487 }
488
489
490 status_t
Flatten(BDataIO * stream) const491 BGradient::Flatten(BDataIO* stream) const
492 {
493 int32 stopCount = CountColorStops();
494 stream->Write(&fType, sizeof(Type));
495 stream->Write(&stopCount, sizeof(int32));
496 if (stopCount > 0) {
497 for (int i = 0; i < stopCount; i++) {
498 stream->Write(ColorStopAtFast(i),
499 sizeof(ColorStop));
500 }
501 }
502
503 switch (fType) {
504 case TYPE_LINEAR:
505 stream->Write(&fData.linear.x1, sizeof(float));
506 stream->Write(&fData.linear.y1, sizeof(float));
507 stream->Write(&fData.linear.x2, sizeof(float));
508 stream->Write(&fData.linear.y2, sizeof(float));
509 break;
510 case TYPE_RADIAL:
511 stream->Write(&fData.radial.cx, sizeof(float));
512 stream->Write(&fData.radial.cy, sizeof(float));
513 stream->Write(&fData.radial.radius, sizeof(float));
514 break;
515 case TYPE_RADIAL_FOCUS:
516 stream->Write(&fData.radial_focus.cx, sizeof(float));
517 stream->Write(&fData.radial_focus.cy, sizeof(float));
518 stream->Write(&fData.radial_focus.fx, sizeof(float));
519 stream->Write(&fData.radial_focus.fy, sizeof(float));
520 stream->Write(&fData.radial_focus.radius, sizeof(float));
521 break;
522 case TYPE_DIAMOND:
523 stream->Write(&fData.diamond.cx, sizeof(float));
524 stream->Write(&fData.diamond.cy, sizeof(float));
525 break;
526 case TYPE_CONIC:
527 stream->Write(&fData.conic.cx, sizeof(float));
528 stream->Write(&fData.conic.cy, sizeof(float));
529 stream->Write(&fData.conic.angle, sizeof(float));
530 break;
531 case TYPE_NONE:
532 break;
533 }
534 return B_OK;
535 }
536
537
538 static BGradient*
gradient_for_type(BGradient::Type type)539 gradient_for_type(BGradient::Type type)
540 {
541 switch (type) {
542 case BGradient::TYPE_LINEAR:
543 return new (std::nothrow) BGradientLinear();
544 case BGradient::TYPE_RADIAL:
545 return new (std::nothrow) BGradientRadial();
546 case BGradient::TYPE_RADIAL_FOCUS:
547 return new (std::nothrow) BGradientRadialFocus();
548 case BGradient::TYPE_DIAMOND:
549 return new (std::nothrow) BGradientDiamond();
550 case BGradient::TYPE_CONIC:
551 return new (std::nothrow) BGradientConic();
552 case BGradient::TYPE_NONE:
553 return new (std::nothrow) BGradient();
554 }
555 return NULL;
556 }
557
558
559 status_t
Unflatten(BGradient * & output,BDataIO * stream)560 BGradient::Unflatten(BGradient *&output, BDataIO* stream)
561 {
562 output = NULL;
563 Type gradientType;
564 int32 colorsCount;
565 stream->Read(&gradientType, sizeof(Type));
566 status_t status = stream->Read(&colorsCount, sizeof(int32));
567 if (status < B_OK)
568 return status;
569
570 ObjectDeleter<BGradient> gradient(gradient_for_type(gradientType));
571 if (!gradient.IsSet())
572 return B_NO_MEMORY;
573
574 if (colorsCount > 0) {
575 ColorStop stop;
576 for (int i = 0; i < colorsCount; i++) {
577 if ((status = stream->Read(&stop, sizeof(ColorStop))) < B_OK)
578 return status;
579 if (!gradient->AddColorStop(stop, i))
580 return B_NO_MEMORY;
581 }
582 }
583
584 switch (gradientType) {
585 case TYPE_LINEAR:
586 stream->Read(&gradient->fData.linear.x1, sizeof(float));
587 stream->Read(&gradient->fData.linear.y1, sizeof(float));
588 stream->Read(&gradient->fData.linear.x2, sizeof(float));
589 if ((status = stream->Read(&gradient->fData.linear.y2, sizeof(float))) < B_OK)
590 return status;
591 break;
592 case TYPE_RADIAL:
593 stream->Read(&gradient->fData.radial.cx, sizeof(float));
594 stream->Read(&gradient->fData.radial.cy, sizeof(float));
595 if ((stream->Read(&gradient->fData.radial.radius, sizeof(float))) < B_OK)
596 return status;
597 break;
598 case TYPE_RADIAL_FOCUS:
599 stream->Read(&gradient->fData.radial_focus.cx, sizeof(float));
600 stream->Read(&gradient->fData.radial_focus.cy, sizeof(float));
601 stream->Read(&gradient->fData.radial_focus.fx, sizeof(float));
602 stream->Read(&gradient->fData.radial_focus.fy, sizeof(float));
603 if ((stream->Read(&gradient->fData.radial_focus.radius, sizeof(float))) < B_OK)
604 return status;
605 break;
606 case TYPE_DIAMOND:
607 stream->Read(&gradient->fData.diamond.cx, sizeof(float));
608 if ((stream->Read(&gradient->fData.diamond.cy, sizeof(float))) < B_OK)
609 return status;
610 break;
611 case TYPE_CONIC:
612 stream->Read(&gradient->fData.conic.cx, sizeof(float));
613 stream->Read(&gradient->fData.conic.cy, sizeof(float));
614 if ((stream->Read(&gradient->fData.conic.angle, sizeof(float))) < B_OK)
615 return status;
616 break;
617 case TYPE_NONE:
618 break;
619 }
620
621 output = gradient.Detach();
622 return B_OK;
623 }
624