xref: /haiku/src/libs/icon/style/GradientTransformable.cpp (revision 36830615ddf125c44964bc21a8c2040058f97841)
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