1 /*
2 * Copyright 2006, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Stephan Aßmus <superstippi@gmx.de>
7 */
8
9 #include "GradientTransformable.h"
10
11 #include <math.h>
12 #include <stdio.h>
13
14 #include <Message.h>
15
16 #ifdef ICON_O_MATIC
17 # include "support.h"
18 #endif
19
20 _USING_ICON_NAMESPACE
21
22
23 // constructor
Gradient(bool empty)24 Gradient::Gradient(bool empty)
25 #ifdef ICON_O_MATIC
26 : BArchivable(),
27 Observable(),
28 BReferenceable(),
29 Transformable(),
30 #else
31 : Transformable(),
32 #endif
33
34 fColors(4),
35 fType(GRADIENT_LINEAR),
36 fInterpolation(INTERPOLATION_SMOOTH),
37 fInheritTransformation(true)
38 {
39 if (!empty) {
40 AddColor(BGradient::ColorStop(0, 0, 0, 255, 0.0), 0);
41 AddColor(BGradient::ColorStop(255, 255, 255, 255, 1.0), 1);
42 }
43 }
44
45 // constructor
Gradient(BMessage * archive)46 Gradient::Gradient(BMessage* archive)
47 #ifdef ICON_O_MATIC
48 : BArchivable(archive),
49 Observable(),
50 BReferenceable(),
51 Transformable(),
52 #else
53 : Transformable(),
54 #endif
55
56 fColors(4),
57 fType(GRADIENT_LINEAR),
58 fInterpolation(INTERPOLATION_SMOOTH),
59 fInheritTransformation(true)
60 {
61 if (!archive)
62 return;
63
64 // read transformation
65 int32 size = Transformable::matrix_size;
66 const void* matrix;
67 ssize_t dataSize = size * sizeof(double);
68 if (archive->FindData("transformation", B_DOUBLE_TYPE,
69 &matrix, &dataSize) == B_OK
70 && dataSize == (ssize_t)(size * sizeof(double)))
71 LoadFrom((const double*)matrix);
72
73 // color steps
74 BGradient::ColorStop step;
75 for (int32 i = 0; archive->FindFloat("offset", i, &step.offset) >= B_OK; i++) {
76 if (archive->FindInt32("color", i, (int32*)&step.color) >= B_OK) {
77 // Use the slower method of adding by offset in case the gradient
78 // was not stored with steps in the correct order
79 AddColor(step.color, step.offset);
80 } else
81 break;
82 }
83 if (archive->FindInt32("type", (int32*)&fType) < B_OK)
84 fType = GRADIENT_LINEAR;
85
86 if (archive->FindInt32("interpolation", (int32*)&fInterpolation) < B_OK)
87 fInterpolation = INTERPOLATION_SMOOTH;
88
89 if (archive->FindBool("inherit transformation",
90 &fInheritTransformation) < B_OK)
91 fInheritTransformation = true;
92 }
93
94 // constructor
Gradient(const Gradient & other)95 Gradient::Gradient(const Gradient& other)
96 #ifdef ICON_O_MATIC
97 : BArchivable(other),
98 Observable(),
99 BReferenceable(),
100 Transformable(other),
101 #else
102 : Transformable(other),
103 #endif
104
105 fColors(4),
106 fType(other.fType),
107 fInterpolation(other.fInterpolation),
108 fInheritTransformation(other.fInheritTransformation)
109 {
110 for (int32 i = 0; BGradient::ColorStop* step = other.ColorAt(i); i++) {
111 AddColor(*step, i);
112 }
113 }
114
115 // destructor
~Gradient()116 Gradient::~Gradient()
117 {
118 _MakeEmpty();
119 }
120
121 #ifdef ICON_O_MATIC
122 // Archive
123 status_t
Archive(BMessage * into,bool deep) const124 Gradient::Archive(BMessage* into, bool deep) const
125 {
126 status_t ret = BArchivable::Archive(into, deep);
127
128 // transformation
129 if (ret == B_OK) {
130 int32 size = Transformable::matrix_size;
131 double matrix[size];
132 StoreTo(matrix);
133 ret = into->AddData("transformation", B_DOUBLE_TYPE,
134 matrix, size * sizeof(double));
135 }
136
137 // color steps
138 if (ret >= B_OK) {
139 for (int32 i = 0; BGradient::ColorStop* step = ColorAt(i); i++) {
140 ret = into->AddInt32("color", (const uint32&)step->color);
141 if (ret < B_OK)
142 break;
143 ret = into->AddFloat("offset", step->offset);
144 if (ret < B_OK)
145 break;
146 }
147 }
148 // gradient and interpolation type
149 if (ret >= B_OK)
150 ret = into->AddInt32("type", (int32)fType);
151 if (ret >= B_OK)
152 ret = into->AddInt32("interpolation", (int32)fInterpolation);
153 if (ret >= B_OK)
154 ret = into->AddBool("inherit transformation", fInheritTransformation);
155
156 // finish off
157 if (ret >= B_OK)
158 ret = into->AddString("class", "Gradient");
159
160 return ret;
161 }
162 #endif // ICON_O_MATIC
163
164 // #pragma mark -
165
166 // operator=
167 Gradient&
operator =(const Gradient & other)168 Gradient::operator=(const Gradient& other)
169 {
170 #ifdef ICON_O_MATIC
171 AutoNotificationSuspender _(this);
172 #endif
173
174 SetTransform(other);
175 SetColors(other);
176 SetType(other.fType);
177 SetInterpolation(other.fInterpolation);
178 SetInheritTransformation(other.fInheritTransformation);
179
180 return *this;
181 }
182
183 // operator==
184 bool
operator ==(const Gradient & other) const185 Gradient::operator==(const Gradient& other) const
186 {
187 if (Transformable::operator==(other))
188 return ColorStepsAreEqual(other);
189 return false;
190 }
191
192 // operator!=
193 bool
operator !=(const Gradient & other) const194 Gradient::operator!=(const Gradient& other) const
195 {
196 return !(*this == other);
197 }
198
199 // ColorStepsAreEqual
200 bool
ColorStepsAreEqual(const Gradient & other) const201 Gradient::ColorStepsAreEqual(const Gradient& other) const
202 {
203 int32 count = CountColors();
204 if (count == other.CountColors() &&
205 fType == other.fType &&
206 fInterpolation == other.fInterpolation &&
207 fInheritTransformation == other.fInheritTransformation) {
208
209 bool equal = true;
210 for (int32 i = 0; i < count; i++) {
211 BGradient::ColorStop* ourStep = ColorAtFast(i);
212 BGradient::ColorStop* otherStep = other.ColorAtFast(i);
213 if (*ourStep != *otherStep) {
214 equal = false;
215 break;
216 }
217 }
218 return equal;
219 }
220 return false;
221 }
222
223 // SetColors
224 void
SetColors(const Gradient & other)225 Gradient::SetColors(const Gradient& other)
226 {
227 #ifdef ICON_O_MATIC
228 AutoNotificationSuspender _(this);
229 #endif
230
231 _MakeEmpty();
232 for (int32 i = 0; BGradient::ColorStop* step = other.ColorAt(i); i++)
233 AddColor(*step, i);
234
235 Notify();
236 }
237
238 // #pragma mark -
239
240 // AddColor
241 int32
AddColor(const rgb_color & color,float offset)242 Gradient::AddColor(const rgb_color& color, float offset)
243 {
244 // find the correct index (sorted by offset)
245 BGradient::ColorStop* step = new BGradient::ColorStop(color, offset);
246 int32 index = 0;
247 int32 count = CountColors();
248 for (; index < count; index++) {
249 BGradient::ColorStop* s = ColorAtFast(index);
250 if (s->offset > step->offset)
251 break;
252 }
253 if (!fColors.AddItem((void*)step, index)) {
254 delete step;
255 return -1;
256 }
257 Notify();
258 return index;
259 }
260
261 // AddColor
262 bool
AddColor(const BGradient::ColorStop & color,int32 index)263 Gradient::AddColor(const BGradient::ColorStop& color, int32 index)
264 {
265 BGradient::ColorStop* step = new BGradient::ColorStop(color);
266 if (!fColors.AddItem((void*)step, index)) {
267 delete step;
268 return false;
269 }
270 Notify();
271 return true;
272 }
273
274 // RemoveColor
275 bool
RemoveColor(int32 index)276 Gradient::RemoveColor(int32 index)
277 {
278 BGradient::ColorStop* step
279 = (BGradient::ColorStop*)fColors.RemoveItem(index);
280 if (!step) {
281 return false;
282 }
283 delete step;
284 Notify();
285 return true;
286 }
287
288 // #pragma mark -
289
290 // SetColor
291 bool
SetColor(int32 index,const BGradient::ColorStop & color)292 Gradient::SetColor(int32 index, const BGradient::ColorStop& color)
293 {
294 if (BGradient::ColorStop* step = ColorAt(index)) {
295 if (*step != color) {
296 step->color = color.color;
297 step->offset = color.offset;
298 Notify();
299 return true;
300 }
301 }
302 return false;
303 }
304
305 // SetColor
306 bool
SetColor(int32 index,const rgb_color & color)307 Gradient::SetColor(int32 index, const rgb_color& color)
308 {
309 if (BGradient::ColorStop* step = ColorAt(index)) {
310 if ((uint32&)step->color != (uint32&)color) {
311 step->color = color;
312 Notify();
313 return true;
314 }
315 }
316 return false;
317 }
318
319 // SetOffset
320 bool
SetOffset(int32 index,float offset)321 Gradient::SetOffset(int32 index, float offset)
322 {
323 BGradient::ColorStop* step = ColorAt(index);
324 if (step && step->offset != offset) {
325 step->offset = offset;
326 Notify();
327 return true;
328 }
329 return false;
330 }
331
332 // #pragma mark -
333
334 // CountColors
335 int32
CountColors() const336 Gradient::CountColors() const
337 {
338 return fColors.CountItems();
339 }
340
341 // ColorAt
342 BGradient::ColorStop*
ColorAt(int32 index) const343 Gradient::ColorAt(int32 index) const
344 {
345 return (BGradient::ColorStop*)fColors.ItemAt(index);
346 }
347
348 // ColorAtFast
349 BGradient::ColorStop*
ColorAtFast(int32 index) const350 Gradient::ColorAtFast(int32 index) const
351 {
352 return (BGradient::ColorStop*)fColors.ItemAtFast(index);
353 }
354
355 // #pragma mark -
356
357 // SetType
358 void
SetType(gradients_type type)359 Gradient::SetType(gradients_type type)
360 {
361 if (fType != type) {
362 fType = type;
363 Notify();
364 }
365 }
366
367 // SetInterpolation
368 void
SetInterpolation(interpolation_type type)369 Gradient::SetInterpolation(interpolation_type type)
370 {
371 if (fInterpolation != type) {
372 fInterpolation = type;
373 Notify();
374 }
375 }
376
377 // SetInheritTransformation
378 void
SetInheritTransformation(bool inherit)379 Gradient::SetInheritTransformation(bool inherit)
380 {
381 if (fInheritTransformation != inherit) {
382 fInheritTransformation = inherit;
383 Notify();
384 }
385 }
386
387 // #pragma mark -
388
389 // gauss
390 inline double
gauss(double f)391 gauss(double f)
392 {
393 // this aint' a real gauss function
394 if (f > 0.0) {
395 if (f < 0.5)
396 return (1.0 - 2.0 * f*f);
397
398 f = 1.0 - f;
399 return (2.0 * f*f);
400 }
401 return 1.0;
402 }
403
404 // MakeGradient
405 void
MakeGradient(uint32 * colors,int32 count) const406 Gradient::MakeGradient(uint32* colors, int32 count) const
407 {
408 BGradient::ColorStop* from = ColorAt(0);
409
410 if (!from)
411 return;
412
413 // find the step with the lowest offset
414 for (int32 i = 0; BGradient::ColorStop* step = ColorAt(i); i++) {
415 if (step->offset < from->offset)
416 from = step;
417 }
418
419 // current index into "colors" array
420 int32 index = (int32)floorf(count * from->offset + 0.5);
421 if (index < 0)
422 index = 0;
423 if (index > count)
424 index = count;
425 // make sure we fill the entire array
426 if (index > 0) {
427 uint8* c = (uint8*)&colors[0];
428 for (int32 i = 0; i < index; i++) {
429 c[0] = from->color.red;
430 c[1] = from->color.green;
431 c[2] = from->color.blue;
432 c[3] = from->color.alpha;
433 c += 4;
434 }
435 }
436
437 // put all steps that we need to interpolate to into a list
438 BList nextSteps(fColors.CountItems() - 1);
439 for (int32 i = 0; BGradient::ColorStop* step = ColorAt(i); i++) {
440 if (step != from)
441 nextSteps.AddItem((void*)step);
442 }
443
444 // interpolate "from" to "to"
445 while (!nextSteps.IsEmpty()) {
446
447 // find the step with the next offset
448 BGradient::ColorStop* to = NULL;
449 float nextOffsetDist = 2.0;
450 for (int32 i = 0; BGradient::ColorStop* step
451 = (BGradient::ColorStop*)nextSteps.ItemAt(i); i++) {
452 float d = step->offset - from->offset;
453 if (d < nextOffsetDist && d >= 0) {
454 to = step;
455 nextOffsetDist = d;
456 }
457 }
458 if (!to)
459 break;
460
461 nextSteps.RemoveItem((void*)to);
462
463 // interpolate
464 int32 offset = (int32)floorf((count - 1) * to->offset + 0.5);
465 if (offset >= count)
466 offset = count - 1;
467 int32 dist = offset - index;
468 if (dist >= 0) {
469 uint8* c = (uint8*)&colors[index];
470 #if GAMMA_BLEND
471 uint16 fromRed = kGammaTable[from->color.red];
472 uint16 fromGreen = kGammaTable[from->color.green];
473 uint16 fromBlue = kGammaTable[from->color.blue];
474 uint16 toRed = kGammaTable[to->color.red];
475 uint16 toGreen = kGammaTable[to->color.green];
476 uint16 toBlue = kGammaTable[to->color.blue];
477
478 for (int32 i = index; i <= offset; i++) {
479 float f = (float)(offset - i) / (float)(dist + 1);
480 if (fInterpolation == INTERPOLATION_SMOOTH)
481 f = gauss(1.0 - f);
482 float t = 1.0 - f;
483 c[0] = kInverseGammaTable[(uint16)floor(fromBlue * f + toBlue * t + 0.5)];
484 c[1] = kInverseGammaTable[(uint16)floor(fromGreen * f + toGreen * t + 0.5)];
485 c[2] = kInverseGammaTable[(uint16)floor(fromRed * f + toRed * t + 0.5)];
486 c[3] = (uint8)floor(from->color.alpha * f + to->color.alpha * t + 0.5);
487 c += 4;
488 }
489 #else // GAMMA_BLEND
490 for (int32 i = index; i <= offset; i++) {
491 float f = (float)(offset - i) / (float)(dist + 1);
492 if (fInterpolation == INTERPOLATION_SMOOTH)
493 f = gauss(1.0 - f);
494 float t = 1.0 - f;
495 c[0] = (uint8)floor(from->color.red * f + to->color.red * t + 0.5);
496 c[1] = (uint8)floor(from->color.green * f + to->color.green * t + 0.5);
497 c[2] = (uint8)floor(from->color.blue * f + to->color.blue * t + 0.5);
498 c[3] = (uint8)floor(from->color.alpha * f + to->color.alpha * t + 0.5);
499 c += 4;
500 }
501 #endif // GAMMA_BLEND
502 }
503 index = offset + 1;
504 // the current "to" will be the "from" in the next interpolation
505 from = to;
506 }
507 // make sure we fill the entire array
508 if (index < count) {
509 uint8* c = (uint8*)&colors[index];
510 for (int32 i = index; i < count; i++) {
511 c[0] = from->color.red;
512 c[1] = from->color.green;
513 c[2] = from->color.blue;
514 c[3] = from->color.alpha;
515 c += 4;
516 }
517 }
518 }
519
520 // FitToBounds
521 void
FitToBounds(const BRect & bounds)522 Gradient::FitToBounds(const BRect& bounds)
523 {
524 double parl[6];
525 parl[0] = bounds.left;
526 parl[1] = bounds.top;
527 parl[2] = bounds.right;
528 parl[3] = bounds.top;
529 parl[4] = bounds.right;
530 parl[5] = bounds.bottom;
531 agg::trans_affine transform(-200.0, -200.0, 200.0, 200.0, parl);
532 multiply(transform);
533 }
534
535 // string_for_type
536 static const char*
string_for_type(gradients_type type)537 string_for_type(gradients_type type)
538 {
539 switch (type) {
540 case GRADIENT_LINEAR:
541 return "GRADIENT_LINEAR";
542 case GRADIENT_CIRCULAR:
543 return "GRADIENT_CIRCULAR";
544 case GRADIENT_DIAMOND:
545 return "GRADIENT_DIAMOND";
546 case GRADIENT_CONIC:
547 return "GRADIENT_CONIC";
548 case GRADIENT_XY:
549 return "GRADIENT_XY";
550 case GRADIENT_SQRT_XY:
551 return "GRADIENT_SQRT_XY";
552 }
553 return "<unkown>";
554 }
555
556 //string_for_interpolation
557 static const char*
string_for_interpolation(interpolation_type type)558 string_for_interpolation(interpolation_type type)
559 {
560 switch (type) {
561 case INTERPOLATION_LINEAR:
562 return "INTERPOLATION_LINEAR";
563 case INTERPOLATION_SMOOTH:
564 return "INTERPOLATION_SMOOTH";
565 }
566 return "<unkown>";
567 }
568
569 // GradientArea
570 BRect
GradientArea() const571 Gradient::GradientArea() const
572 {
573 BRect area(0, 0, 64, 64);
574 switch (fType) {
575 case GRADIENT_LINEAR:
576 case GRADIENT_CIRCULAR:
577 case GRADIENT_DIAMOND:
578 case GRADIENT_CONIC:
579 case GRADIENT_XY:
580 case GRADIENT_SQRT_XY:
581 break;
582 }
583 return area;
584 }
585
586 // TransformationChanged()
587 void
TransformationChanged()588 Gradient::TransformationChanged()
589 {
590 Notify();
591 }
592
593 // PrintToStream
594 void
PrintToStream() const595 Gradient::PrintToStream() const
596 {
597 printf("Gradient: type: %s, interpolation: %s, inherits transform: %d\n",
598 string_for_type(fType),
599 string_for_interpolation(fInterpolation),
600 fInheritTransformation);
601 for (int32 i = 0; BGradient::ColorStop* step = ColorAt(i); i++) {
602 printf(" %" B_PRId32 ": offset: %.1f -> color(%d, %d, %d, %d)\n",
603 i, step->offset,
604 step->color.red,
605 step->color.green,
606 step->color.blue,
607 step->color.alpha);
608 }
609
610 Transformable::PrintToStream();
611 }
612
613 // _MakeEmpty
614 void
_MakeEmpty()615 Gradient::_MakeEmpty()
616 {
617 int32 count = CountColors();
618 for (int32 i = 0; i < count; i++)
619 delete ColorAtFast(i);
620 fColors.MakeEmpty();
621 }
622