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