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