Make UEFI boot-platform build again
[haiku.git] / src / libs / icon / style / GradientTransformable.cpp
blob0ce4c3f739a2dd8c99dc13f2246afdc27ead3059
1 /*
2 * Copyright 2006, Haiku.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Stephan Aßmus <superstippi@gmx.de>
7 */
9 #include "GradientTransformable.h"
11 #include <math.h>
12 #include <stdio.h>
14 #include <Message.h>
16 #ifdef ICON_O_MATIC
17 # include "support.h"
18 #endif
20 // constructor
21 Gradient::Gradient(bool empty)
22 #ifdef ICON_O_MATIC
23 : BArchivable(),
24 Observable(),
25 BReferenceable(),
26 Transformable(),
27 #else
28 : Transformable(),
29 #endif
31 fColors(4),
32 fType(GRADIENT_LINEAR),
33 fInterpolation(INTERPOLATION_SMOOTH),
34 fInheritTransformation(true)
36 if (!empty) {
37 AddColor(BGradient::ColorStop(0, 0, 0, 255, 0.0), 0);
38 AddColor(BGradient::ColorStop(255, 255, 255, 255, 1.0), 1);
42 // constructor
43 Gradient::Gradient(BMessage* archive)
44 #ifdef ICON_O_MATIC
45 : BArchivable(archive),
46 Observable(),
47 BReferenceable(),
48 Transformable(),
49 #else
50 : Transformable(),
51 #endif
53 fColors(4),
54 fType(GRADIENT_LINEAR),
55 fInterpolation(INTERPOLATION_SMOOTH),
56 fInheritTransformation(true)
58 if (!archive)
59 return;
61 // read transformation
62 int32 size = Transformable::matrix_size;
63 const void* matrix;
64 ssize_t dataSize = size * sizeof(double);
65 if (archive->FindData("transformation", B_DOUBLE_TYPE,
66 &matrix, &dataSize) == B_OK
67 && dataSize == (ssize_t)(size * sizeof(double)))
68 LoadFrom((const double*)matrix);
70 // color steps
71 BGradient::ColorStop step;
72 for (int32 i = 0; archive->FindFloat("offset", i, &step.offset) >= B_OK; i++) {
73 if (archive->FindInt32("color", i, (int32*)&step.color) >= B_OK)
74 AddColor(step, i);
75 else
76 break;
78 if (archive->FindInt32("type", (int32*)&fType) < B_OK)
79 fType = GRADIENT_LINEAR;
81 if (archive->FindInt32("interpolation", (int32*)&fInterpolation) < B_OK)
82 fInterpolation = INTERPOLATION_SMOOTH;
84 if (archive->FindBool("inherit transformation",
85 &fInheritTransformation) < B_OK)
86 fInheritTransformation = true;
89 // constructor
90 Gradient::Gradient(const Gradient& other)
91 #ifdef ICON_O_MATIC
92 : BArchivable(other),
93 Observable(),
94 BReferenceable(),
95 Transformable(other),
96 #else
97 : Transformable(other),
98 #endif
100 fColors(4),
101 fType(other.fType),
102 fInterpolation(other.fInterpolation),
103 fInheritTransformation(other.fInheritTransformation)
105 for (int32 i = 0; BGradient::ColorStop* step = other.ColorAt(i); i++) {
106 AddColor(*step, i);
110 // destructor
111 Gradient::~Gradient()
113 _MakeEmpty();
116 #ifdef ICON_O_MATIC
117 // Archive
118 status_t
119 Gradient::Archive(BMessage* into, bool deep) const
121 status_t ret = BArchivable::Archive(into, deep);
123 // transformation
124 if (ret == B_OK) {
125 int32 size = Transformable::matrix_size;
126 double matrix[size];
127 StoreTo(matrix);
128 ret = into->AddData("transformation", B_DOUBLE_TYPE,
129 matrix, size * sizeof(double));
132 // color steps
133 if (ret >= B_OK) {
134 for (int32 i = 0; BGradient::ColorStop* step = ColorAt(i); i++) {
135 ret = into->AddInt32("color", (const uint32&)step->color);
136 if (ret < B_OK)
137 break;
138 ret = into->AddFloat("offset", step->offset);
139 if (ret < B_OK)
140 break;
143 // gradient and interpolation type
144 if (ret >= B_OK)
145 ret = into->AddInt32("type", (int32)fType);
146 if (ret >= B_OK)
147 ret = into->AddInt32("interpolation", (int32)fInterpolation);
148 if (ret >= B_OK)
149 ret = into->AddBool("inherit transformation", fInheritTransformation);
151 // finish off
152 if (ret >= B_OK)
153 ret = into->AddString("class", "Gradient");
155 return ret;
157 #endif // ICON_O_MATIC
159 // #pragma mark -
161 // operator=
162 Gradient&
163 Gradient::operator=(const Gradient& other)
165 #ifdef ICON_O_MATIC
166 AutoNotificationSuspender _(this);
167 #endif
169 SetTransform(other);
170 SetColors(other);
171 SetType(other.fType);
172 SetInterpolation(other.fInterpolation);
173 SetInheritTransformation(other.fInheritTransformation);
175 return *this;
178 // operator==
179 bool
180 Gradient::operator==(const Gradient& other) const
182 if (Transformable::operator==(other))
183 return ColorStepsAreEqual(other);
184 return false;
187 // operator!=
188 bool
189 Gradient::operator!=(const Gradient& other) const
191 return !(*this == other);
194 // ColorStepsAreEqual
195 bool
196 Gradient::ColorStepsAreEqual(const Gradient& other) const
198 int32 count = CountColors();
199 if (count == other.CountColors() &&
200 fType == other.fType &&
201 fInterpolation == other.fInterpolation &&
202 fInheritTransformation == other.fInheritTransformation) {
204 bool equal = true;
205 for (int32 i = 0; i < count; i++) {
206 BGradient::ColorStop* ourStep = ColorAtFast(i);
207 BGradient::ColorStop* otherStep = other.ColorAtFast(i);
208 if (*ourStep != *otherStep) {
209 equal = false;
210 break;
213 return equal;
215 return false;
218 // SetColors
219 void
220 Gradient::SetColors(const Gradient& other)
222 #ifdef ICON_O_MATIC
223 AutoNotificationSuspender _(this);
224 #endif
226 _MakeEmpty();
227 for (int32 i = 0; BGradient::ColorStop* step = other.ColorAt(i); i++)
228 AddColor(*step, i);
230 Notify();
233 // #pragma mark -
235 // AddColor
236 int32
237 Gradient::AddColor(const rgb_color& color, float offset)
239 // find the correct index (sorted by offset)
240 BGradient::ColorStop* step = new BGradient::ColorStop(color, offset);
241 int32 index = 0;
242 int32 count = CountColors();
243 for (; index < count; index++) {
244 BGradient::ColorStop* s = ColorAtFast(index);
245 if (s->offset > step->offset)
246 break;
248 if (!fColors.AddItem((void*)step, index)) {
249 delete step;
250 return -1;
252 Notify();
253 return index;
256 // AddColor
257 bool
258 Gradient::AddColor(const BGradient::ColorStop& color, int32 index)
260 BGradient::ColorStop* step = new BGradient::ColorStop(color);
261 if (!fColors.AddItem((void*)step, index)) {
262 delete step;
263 return false;
265 Notify();
266 return true;
269 // RemoveColor
270 bool
271 Gradient::RemoveColor(int32 index)
273 BGradient::ColorStop* step
274 = (BGradient::ColorStop*)fColors.RemoveItem(index);
275 if (!step) {
276 return false;
278 delete step;
279 Notify();
280 return true;
283 // #pragma mark -
285 // SetColor
286 bool
287 Gradient::SetColor(int32 index, const BGradient::ColorStop& color)
289 if (BGradient::ColorStop* step = ColorAt(index)) {
290 if (*step != color) {
291 step->color = color.color;
292 step->offset = color.offset;
293 Notify();
294 return true;
297 return false;
300 // SetColor
301 bool
302 Gradient::SetColor(int32 index, const rgb_color& color)
304 if (BGradient::ColorStop* step = ColorAt(index)) {
305 if ((uint32&)step->color != (uint32&)color) {
306 step->color = color;
307 Notify();
308 return true;
311 return false;
314 // SetOffset
315 bool
316 Gradient::SetOffset(int32 index, float offset)
318 BGradient::ColorStop* step = ColorAt(index);
319 if (step && step->offset != offset) {
320 step->offset = offset;
321 Notify();
322 return true;
324 return false;
327 // #pragma mark -
329 // CountColors
330 int32
331 Gradient::CountColors() const
333 return fColors.CountItems();
336 // ColorAt
337 BGradient::ColorStop*
338 Gradient::ColorAt(int32 index) const
340 return (BGradient::ColorStop*)fColors.ItemAt(index);
343 // ColorAtFast
344 BGradient::ColorStop*
345 Gradient::ColorAtFast(int32 index) const
347 return (BGradient::ColorStop*)fColors.ItemAtFast(index);
350 // #pragma mark -
352 // SetType
353 void
354 Gradient::SetType(gradients_type type)
356 if (fType != type) {
357 fType = type;
358 Notify();
362 // SetInterpolation
363 void
364 Gradient::SetInterpolation(interpolation_type type)
366 if (fInterpolation != type) {
367 fInterpolation = type;
368 Notify();
372 // SetInheritTransformation
373 void
374 Gradient::SetInheritTransformation(bool inherit)
376 if (fInheritTransformation != inherit) {
377 fInheritTransformation = inherit;
378 Notify();
382 // #pragma mark -
384 // gauss
385 inline double
386 gauss(double f)
388 // this aint' a real gauss function
389 if (f > 0.0) {
390 if (f < 0.5)
391 return (1.0 - 2.0 * f*f);
393 f = 1.0 - f;
394 return (2.0 * f*f);
396 return 1.0;
399 // MakeGradient
400 void
401 Gradient::MakeGradient(uint32* colors, int32 count) const
403 BGradient::ColorStop* from = ColorAt(0);
405 if (!from)
406 return;
408 // find the step with the lowest offset
409 for (int32 i = 0; BGradient::ColorStop* step = ColorAt(i); i++) {
410 if (step->offset < from->offset)
411 from = step;
414 // current index into "colors" array
415 int32 index = (int32)floorf(count * from->offset + 0.5);
416 if (index < 0)
417 index = 0;
418 if (index > count)
419 index = count;
420 // make sure we fill the entire array
421 if (index > 0) {
422 uint8* c = (uint8*)&colors[0];
423 for (int32 i = 0; i < index; i++) {
424 c[0] = from->color.red;
425 c[1] = from->color.green;
426 c[2] = from->color.blue;
427 c[3] = from->color.alpha;
428 c += 4;
432 // put all steps that we need to interpolate to into a list
433 BList nextSteps(fColors.CountItems() - 1);
434 for (int32 i = 0; BGradient::ColorStop* step = ColorAt(i); i++) {
435 if (step != from)
436 nextSteps.AddItem((void*)step);
439 // interpolate "from" to "to"
440 while (!nextSteps.IsEmpty()) {
442 // find the step with the next offset
443 BGradient::ColorStop* to = NULL;
444 float nextOffsetDist = 2.0;
445 for (int32 i = 0; BGradient::ColorStop* step
446 = (BGradient::ColorStop*)nextSteps.ItemAt(i); i++) {
447 float d = step->offset - from->offset;
448 if (d < nextOffsetDist && d >= 0) {
449 to = step;
450 nextOffsetDist = d;
453 if (!to)
454 break;
456 nextSteps.RemoveItem((void*)to);
458 // interpolate
459 int32 offset = (int32)floorf((count - 1) * to->offset + 0.5);
460 if (offset >= count)
461 offset = count - 1;
462 int32 dist = offset - index;
463 if (dist >= 0) {
464 uint8* c = (uint8*)&colors[index];
465 #if GAMMA_BLEND
466 uint16 fromRed = kGammaTable[from->color.red];
467 uint16 fromGreen = kGammaTable[from->color.green];
468 uint16 fromBlue = kGammaTable[from->color.blue];
469 uint16 toRed = kGammaTable[to->color.red];
470 uint16 toGreen = kGammaTable[to->color.green];
471 uint16 toBlue = kGammaTable[to->color.blue];
473 for (int32 i = index; i <= offset; i++) {
474 float f = (float)(offset - i) / (float)(dist + 1);
475 if (fInterpolation == INTERPOLATION_SMOOTH)
476 f = gauss(1.0 - f);
477 float t = 1.0 - f;
478 c[0] = kInverseGammaTable[(uint16)floor(fromBlue * f + toBlue * t + 0.5)];
479 c[1] = kInverseGammaTable[(uint16)floor(fromGreen * f + toGreen * t + 0.5)];
480 c[2] = kInverseGammaTable[(uint16)floor(fromRed * f + toRed * t + 0.5)];
481 c[3] = (uint8)floor(from->color.alpha * f + to->color.alpha * t + 0.5);
482 c += 4;
484 #else // GAMMA_BLEND
485 for (int32 i = index; i <= offset; i++) {
486 float f = (float)(offset - i) / (float)(dist + 1);
487 if (fInterpolation == INTERPOLATION_SMOOTH)
488 f = gauss(1.0 - f);
489 float t = 1.0 - f;
490 c[0] = (uint8)floor(from->color.red * f + to->color.red * t + 0.5);
491 c[1] = (uint8)floor(from->color.green * f + to->color.green * t + 0.5);
492 c[2] = (uint8)floor(from->color.blue * f + to->color.blue * t + 0.5);
493 c[3] = (uint8)floor(from->color.alpha * f + to->color.alpha * t + 0.5);
494 c += 4;
496 #endif // GAMMA_BLEND
498 index = offset + 1;
499 // the current "to" will be the "from" in the next interpolation
500 from = to;
502 // make sure we fill the entire array
503 if (index < count) {
504 uint8* c = (uint8*)&colors[index];
505 for (int32 i = index; i < count; i++) {
506 c[0] = from->color.red;
507 c[1] = from->color.green;
508 c[2] = from->color.blue;
509 c[3] = from->color.alpha;
510 c += 4;
515 // FitToBounds
516 void
517 Gradient::FitToBounds(const BRect& bounds)
519 double parl[6];
520 parl[0] = bounds.left;
521 parl[1] = bounds.top;
522 parl[2] = bounds.right;
523 parl[3] = bounds.top;
524 parl[4] = bounds.right;
525 parl[5] = bounds.bottom;
526 agg::trans_affine transform(-200.0, -200.0, 200.0, 200.0, parl);
527 multiply(transform);
530 // string_for_type
531 static const char*
532 string_for_type(gradients_type type)
534 switch (type) {
535 case GRADIENT_LINEAR:
536 return "GRADIENT_LINEAR";
537 case GRADIENT_CIRCULAR:
538 return "GRADIENT_CIRCULAR";
539 case GRADIENT_DIAMOND:
540 return "GRADIENT_DIAMOND";
541 case GRADIENT_CONIC:
542 return "GRADIENT_CONIC";
543 case GRADIENT_XY:
544 return "GRADIENT_XY";
545 case GRADIENT_SQRT_XY:
546 return "GRADIENT_SQRT_XY";
548 return "<unkown>";
551 //string_for_interpolation
552 static const char*
553 string_for_interpolation(interpolation_type type)
555 switch (type) {
556 case INTERPOLATION_LINEAR:
557 return "INTERPOLATION_LINEAR";
558 case INTERPOLATION_SMOOTH:
559 return "INTERPOLATION_SMOOTH";
561 return "<unkown>";
564 // GradientArea
565 BRect
566 Gradient::GradientArea() const
568 BRect area(0, 0, 64, 64);
569 switch (fType) {
570 case GRADIENT_LINEAR:
571 case GRADIENT_CIRCULAR:
572 case GRADIENT_DIAMOND:
573 case GRADIENT_CONIC:
574 case GRADIENT_XY:
575 case GRADIENT_SQRT_XY:
576 break;
578 return area;
581 // TransformationChanged()
582 void
583 Gradient::TransformationChanged()
585 Notify();
588 // PrintToStream
589 void
590 Gradient::PrintToStream() const
592 printf("Gradient: type: %s, interpolation: %s, inherits transform: %d\n",
593 string_for_type(fType),
594 string_for_interpolation(fInterpolation),
595 fInheritTransformation);
596 for (int32 i = 0; BGradient::ColorStop* step = ColorAt(i); i++) {
597 printf(" %" B_PRId32 ": offset: %.1f -> color(%d, %d, %d, %d)\n",
598 i, step->offset,
599 step->color.red,
600 step->color.green,
601 step->color.blue,
602 step->color.alpha);
606 // _MakeEmpty
607 void
608 Gradient::_MakeEmpty()
610 int32 count = CountColors();
611 for (int32 i = 0; i < count; i++)
612 delete ColorAtFast(i);
613 fColors.MakeEmpty();