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
24Gradient::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
46Gradient::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
95Gradient::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
116Gradient::~Gradient()
117{
118	_MakeEmpty();
119}
120
121#ifdef ICON_O_MATIC
122// Archive
123status_t
124Gradient::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=
167Gradient&
168Gradient::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==
184bool
185Gradient::operator==(const Gradient& other) const
186{
187	if (Transformable::operator==(other))
188		return ColorStepsAreEqual(other);
189	return false;
190}
191
192// operator!=
193bool
194Gradient::operator!=(const Gradient& other) const
195{
196	return !(*this == other);
197}
198
199// ColorStepsAreEqual
200bool
201Gradient::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
224void
225Gradient::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
241int32
242Gradient::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
262bool
263Gradient::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
275bool
276Gradient::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
291bool
292Gradient::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
306bool
307Gradient::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
320bool
321Gradient::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
335int32
336Gradient::CountColors() const
337{
338	return fColors.CountItems();
339}
340
341// ColorAt
342BGradient::ColorStop*
343Gradient::ColorAt(int32 index) const
344{
345	return (BGradient::ColorStop*)fColors.ItemAt(index);
346}
347
348// ColorAtFast
349BGradient::ColorStop*
350Gradient::ColorAtFast(int32 index) const
351{
352	return (BGradient::ColorStop*)fColors.ItemAtFast(index);
353}
354
355// #pragma mark -
356
357// SetType
358void
359Gradient::SetType(gradients_type type)
360{
361	if (fType != type) {
362		fType = type;
363		Notify();
364	}
365}
366
367// SetInterpolation
368void
369Gradient::SetInterpolation(interpolation_type type)
370{
371	if (fInterpolation != type) {
372		fInterpolation = type;
373		Notify();
374	}
375}
376
377// SetInheritTransformation
378void
379Gradient::SetInheritTransformation(bool inherit)
380{
381	if (fInheritTransformation != inherit) {
382		fInheritTransformation = inherit;
383		Notify();
384	}
385}
386
387// #pragma mark -
388
389// gauss
390inline double
391gauss(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
405void
406Gradient::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
521void
522Gradient::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
536static const char*
537string_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
557static const char*
558string_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
570BRect
571Gradient::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()
587void
588Gradient::TransformationChanged()
589{
590	Notify();
591}
592
593// PrintToStream
594void
595Gradient::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
614void
615Gradient::_MakeEmpty()
616{
617	int32 count = CountColors();
618	for (int32 i = 0; i < count; i++)
619		delete ColorAtFast(i);
620	fColors.MakeEmpty();
621}
622