xref: /haiku/src/libs/icon/style/GradientTransformable.cpp (revision 1a3518cf757c2da8006753f83962da5935bbc82b)
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 			AddColor(step, i);
75 		else
76 			break;
77 	}
78 	if (archive->FindInt32("type", (int32*)&fType) < B_OK)
79 		fType = GRADIENT_LINEAR;
80 
81 	if (archive->FindInt32("interpolation", (int32*)&fInterpolation) < B_OK)
82 		fInterpolation = INTERPOLATION_SMOOTH;
83 
84 	if (archive->FindBool("inherit transformation",
85 						  &fInheritTransformation) < B_OK)
86 		fInheritTransformation = true;
87 }
88 
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
99 
100 	  fColors(4),
101 	  fType(other.fType),
102 	  fInterpolation(other.fInterpolation),
103 	  fInheritTransformation(other.fInheritTransformation)
104 {
105 	for (int32 i = 0; BGradient::ColorStop* step = other.ColorAt(i); i++) {
106 		AddColor(*step, i);
107 	}
108 }
109 
110 // destructor
111 Gradient::~Gradient()
112 {
113 	_MakeEmpty();
114 }
115 
116 #ifdef ICON_O_MATIC
117 // Archive
118 status_t
119 Gradient::Archive(BMessage* into, bool deep) const
120 {
121 	status_t ret = BArchivable::Archive(into, deep);
122 
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));
130 	}
131 
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;
141 		}
142 	}
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);
150 
151 	// finish off
152 	if (ret >= B_OK)
153 		ret = into->AddString("class", "Gradient");
154 
155 	return ret;
156 }
157 #endif // ICON_O_MATIC
158 
159 // #pragma mark -
160 
161 // operator=
162 Gradient&
163 Gradient::operator=(const Gradient& other)
164 {
165 #ifdef ICON_O_MATIC
166 	AutoNotificationSuspender _(this);
167 #endif
168 
169 	SetTransform(other);
170 	SetColors(other);
171 	SetType(other.fType);
172 	SetInterpolation(other.fInterpolation);
173 	SetInheritTransformation(other.fInheritTransformation);
174 
175 	return *this;
176 }
177 
178 // operator==
179 bool
180 Gradient::operator==(const Gradient& other) const
181 {
182 	if (Transformable::operator==(other))
183 		return ColorStepsAreEqual(other);
184 	return false;
185 }
186 
187 // operator!=
188 bool
189 Gradient::operator!=(const Gradient& other) const
190 {
191 	return !(*this == other);
192 }
193 
194 // ColorStepsAreEqual
195 bool
196 Gradient::ColorStepsAreEqual(const Gradient& other) const
197 {
198 	int32 count = CountColors();
199 	if (count == other.CountColors() &&
200 		fType == other.fType &&
201 		fInterpolation == other.fInterpolation &&
202 		fInheritTransformation == other.fInheritTransformation) {
203 
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;
211 			}
212 		}
213 		return equal;
214 	}
215 	return false;
216 }
217 
218 // SetColors
219 void
220 Gradient::SetColors(const Gradient& other)
221 {
222 #ifdef ICON_O_MATIC
223 	AutoNotificationSuspender _(this);
224 #endif
225 
226 	_MakeEmpty();
227 	for (int32 i = 0; BGradient::ColorStop* step = other.ColorAt(i); i++)
228 		AddColor(*step, i);
229 
230 	Notify();
231 }
232 
233 // #pragma mark -
234 
235 // AddColor
236 int32
237 Gradient::AddColor(const rgb_color& color, float offset)
238 {
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;
247 	}
248 	if (!fColors.AddItem((void*)step, index)) {
249 		delete step;
250 		return -1;
251 	}
252 	Notify();
253 	return index;
254 }
255 
256 // AddColor
257 bool
258 Gradient::AddColor(const BGradient::ColorStop& color, int32 index)
259 {
260 	BGradient::ColorStop* step = new BGradient::ColorStop(color);
261 	if (!fColors.AddItem((void*)step, index)) {
262 		delete step;
263 		return false;
264 	}
265 	Notify();
266 	return true;
267 }
268 
269 // RemoveColor
270 bool
271 Gradient::RemoveColor(int32 index)
272 {
273 	BGradient::ColorStop* step
274 		= (BGradient::ColorStop*)fColors.RemoveItem(index);
275 	if (!step) {
276 		return false;
277 	}
278 	delete step;
279 	Notify();
280 	return true;
281 }
282 
283 // #pragma mark -
284 
285 // SetColor
286 bool
287 Gradient::SetColor(int32 index, const BGradient::ColorStop& color)
288 {
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;
295 		}
296 	}
297 	return false;
298 }
299 
300 // SetColor
301 bool
302 Gradient::SetColor(int32 index, const rgb_color& color)
303 {
304 	if (BGradient::ColorStop* step = ColorAt(index)) {
305 		if ((uint32&)step->color != (uint32&)color) {
306 			step->color = color;
307 			Notify();
308 			return true;
309 		}
310 	}
311 	return false;
312 }
313 
314 // SetOffset
315 bool
316 Gradient::SetOffset(int32 index, float offset)
317 {
318 	BGradient::ColorStop* step = ColorAt(index);
319 	if (step && step->offset != offset) {
320 		step->offset = offset;
321 		Notify();
322 		return true;
323 	}
324 	return false;
325 }
326 
327 // #pragma mark -
328 
329 // CountColors
330 int32
331 Gradient::CountColors() const
332 {
333 	return fColors.CountItems();
334 }
335 
336 // ColorAt
337 BGradient::ColorStop*
338 Gradient::ColorAt(int32 index) const
339 {
340 	return (BGradient::ColorStop*)fColors.ItemAt(index);
341 }
342 
343 // ColorAtFast
344 BGradient::ColorStop*
345 Gradient::ColorAtFast(int32 index) const
346 {
347 	return (BGradient::ColorStop*)fColors.ItemAtFast(index);
348 }
349 
350 // #pragma mark -
351 
352 // SetType
353 void
354 Gradient::SetType(gradients_type type)
355 {
356 	if (fType != type) {
357 		fType = type;
358 		Notify();
359 	}
360 }
361 
362 // SetInterpolation
363 void
364 Gradient::SetInterpolation(interpolation_type type)
365 {
366 	if (fInterpolation != type) {
367 		fInterpolation = type;
368 		Notify();
369 	}
370 }
371 
372 // SetInheritTransformation
373 void
374 Gradient::SetInheritTransformation(bool inherit)
375 {
376 	if (fInheritTransformation != inherit) {
377 		fInheritTransformation = inherit;
378 		Notify();
379 	}
380 }
381 
382 // #pragma mark -
383 
384 // gauss
385 inline double
386 gauss(double f)
387 {
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);
392 
393 		f = 1.0 - f;
394 		return (2.0 * f*f);
395 	}
396 	return 1.0;
397 }
398 
399 // MakeGradient
400 void
401 Gradient::MakeGradient(uint32* colors, int32 count) const
402 {
403 	BGradient::ColorStop* from = ColorAt(0);
404 
405 	if (!from)
406 		return;
407 
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;
412 	}
413 
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;
429 		}
430 	}
431 
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);
437 	}
438 
439 	// interpolate "from" to "to"
440 	while (!nextSteps.IsEmpty()) {
441 
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;
451 			}
452 		}
453 		if (!to)
454 			break;
455 
456 		nextSteps.RemoveItem((void*)to);
457 
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];
472 
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;
483 			}
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;
495 			}
496 #endif // GAMMA_BLEND
497 		}
498 		index = offset + 1;
499 		// the current "to" will be the "from" in the next interpolation
500 		from = to;
501 	}
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;
511 		}
512 	}
513 }
514 
515 // FitToBounds
516 void
517 Gradient::FitToBounds(const BRect& bounds)
518 {
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);
528 }
529 
530 // string_for_type
531 static const char*
532 string_for_type(gradients_type type)
533 {
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";
547 	}
548 	return "<unkown>";
549 }
550 
551 //string_for_interpolation
552 static const char*
553 string_for_interpolation(interpolation_type type)
554 {
555 	switch (type) {
556 		case INTERPOLATION_LINEAR:
557 			return "INTERPOLATION_LINEAR";
558 		case INTERPOLATION_SMOOTH:
559 			return "INTERPOLATION_SMOOTH";
560 	}
561 	return "<unkown>";
562 }
563 
564 // GradientArea
565 BRect
566 Gradient::GradientArea() const
567 {
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;
577 	}
578 	return area;
579 }
580 
581 // TransformationChanged()
582 void
583 Gradient::TransformationChanged()
584 {
585 	Notify();
586 }
587 
588 // PrintToStream
589 void
590 Gradient::PrintToStream() const
591 {
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);
603 	}
604 
605 	Transformable::PrintToStream();
606 }
607 
608 // _MakeEmpty
609 void
610 Gradient::_MakeEmpty()
611 {
612 	int32 count = CountColors();
613 	for (int32 i = 0; i < count; i++)
614 		delete ColorAtFast(i);
615 	fColors.MakeEmpty();
616 }
617