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
21Gradient::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
42Gradient::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
88Gradient::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
108Gradient::~Gradient()
109{
110	_MakeEmpty();
111}
112
113#ifdef ICON_O_MATIC
114// Archive
115status_t
116Gradient::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=
159Gradient&
160Gradient::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==
176bool
177Gradient::operator==(const Gradient& other) const
178{
179	if (Transformable::operator==(other))
180		return ColorStepsAreEqual(other);
181	return false;
182}
183
184// operator!=
185bool
186Gradient::operator!=(const Gradient& other) const
187{
188	return !(*this == other);
189}
190
191// ColorStepsAreEqual
192bool
193Gradient::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
216void
217Gradient::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
233int32
234Gradient::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
254bool
255Gradient::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
267bool
268Gradient::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
283bool
284Gradient::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
298bool
299Gradient::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
312bool
313Gradient::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
327int32
328Gradient::CountColors() const
329{
330	return fColors.CountItems();
331}
332
333// ColorAt
334BGradient::ColorStop*
335Gradient::ColorAt(int32 index) const
336{
337	return (BGradient::ColorStop*)fColors.ItemAt(index);
338}
339
340// ColorAtFast
341BGradient::ColorStop*
342Gradient::ColorAtFast(int32 index) const
343{
344	return (BGradient::ColorStop*)fColors.ItemAtFast(index);
345}
346
347// #pragma mark -
348
349// SetType
350void
351Gradient::SetType(gradients_type type)
352{
353	if (fType != type) {
354		fType = type;
355		Notify();
356	}
357}
358
359// SetInterpolation
360void
361Gradient::SetInterpolation(interpolation_type type)
362{
363	if (fInterpolation != type) {
364		fInterpolation = type;
365		Notify();
366	}
367}
368
369// SetInheritTransformation
370void
371Gradient::SetInheritTransformation(bool inherit)
372{
373	if (fInheritTransformation != inherit) {
374		fInheritTransformation = inherit;
375		Notify();
376	}
377}
378
379// #pragma mark -
380
381// gauss
382inline double
383gauss(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
397void
398Gradient::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
513void
514Gradient::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
528static const char*
529string_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
549static const char*
550string_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
562BRect
563Gradient::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()
579void
580Gradient::TransformationChanged()
581{
582	Notify();
583}
584
585// PrintToStream
586void
587Gradient::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("  %" B_PRId32 ": 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
604void
605Gradient::_MakeEmpty()
606{
607	int32 count = CountColors();
608	for (int32 i = 0; i < count; i++)
609		delete ColorAtFast(i);
610	fColors.MakeEmpty();
611}
612