1/*
2 * Copyright 2006-2007, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan A��mus <superstippi@gmx.de>
7 */
8
9#include "IconRenderer.h"
10
11#include <new>
12#include <stdio.h>
13
14#include <Bitmap.h>
15#include <List.h>
16
17#include <agg_span_gradient.h>
18#include <agg_span_interpolator_linear.h>
19
20#include "GradientTransformable.h"
21#include "Icon.h"
22#include "Shape.h"
23#include "ShapeContainer.h"
24#include "Style.h"
25#include "VectorPath.h"
26
27using std::nothrow;
28
29class IconRenderer::StyleHandler {
30	struct StyleItem {
31		Style*			style;
32		Transformation	transformation;
33	};
34
35 public:
36	StyleHandler(::GammaTable& gammaTable)
37		: fStyles(20),
38		  fGammaTable(gammaTable),
39		  fTransparent(0, 0, 0, 0),
40		  fColor(0, 0, 0, 0)
41	{}
42
43	~StyleHandler()
44	{
45		int32 count = fStyles.CountItems();
46		for (int32 i = 0; i < count; i++)
47			delete (StyleItem*)fStyles.ItemAtFast(i);
48	}
49
50	bool is_solid(unsigned styleIndex) const
51	{
52		StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex);
53		if (!styleItem)
54			return true;
55
56		return styleItem->style->Gradient() == NULL;
57	}
58
59	const agg::rgba8& color(unsigned styleIndex);
60
61	void generate_span(agg::rgba8* span, int x, int y,
62					   unsigned len, unsigned styleIndex);
63
64	bool AddStyle(Style* style, const Transformation& transformation)
65	{
66		if (!style)
67			return false;
68		StyleItem* item = new (nothrow) StyleItem;
69		if (!item)
70			return false;
71		item->style = style;
72		// if the style uses a gradient, the transformation
73		// is based on the gradient transformation
74		if (Gradient* gradient = style->Gradient()) {
75			item->transformation = *gradient;
76			item->transformation.multiply(transformation);
77		} else {
78			item->transformation = transformation;
79		}
80		item->transformation.invert();
81		return fStyles.AddItem((void*)item);
82	}
83
84private:
85	template<class GradientFunction>
86	void _GenerateGradient(agg::rgba8* span, int x, int y, unsigned len,
87		GradientFunction function, int32 start, int32 end,
88		const agg::rgba8* gradientColors, Transformation& gradientTransform);
89
90	BList				fStyles;
91	::GammaTable&		fGammaTable;
92	agg::rgba8			fTransparent;
93	agg::rgba8			fColor;
94};
95
96// color
97const agg::rgba8&
98IconRenderer::StyleHandler::color(unsigned styleIndex)
99{
100	StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex);
101	if (!styleItem) {
102		printf("no style at index: %u!\n", styleIndex);
103		return fTransparent;
104	}
105
106	const rgb_color& c = styleItem->style->Color();
107	fColor = agg::rgba8(fGammaTable.dir(c.red), fGammaTable.dir(c.green),
108		fGammaTable.dir(c.blue), c.alpha);
109	fColor.premultiply();
110    return fColor;
111}
112
113// generate_span
114void
115IconRenderer::StyleHandler::generate_span(agg::rgba8* span, int x, int y,
116	unsigned len, unsigned styleIndex)
117{
118	StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex);
119	if (!styleItem || !styleItem->style->Gradient()) {
120		printf("no style/gradient at index: %u!\n", styleIndex);
121		// TODO: memset() span?
122		return;
123	}
124
125	Style* style = styleItem->style;
126	Gradient* gradient = style->Gradient();
127	const agg::rgba8* colors = style->GammaCorrectedColors(fGammaTable);
128
129	switch (gradient->Type()) {
130		case GRADIENT_LINEAR: {
131		    agg::gradient_x function;
132			_GenerateGradient(span, x, y, len, function, -64, 64, colors,
133				styleItem->transformation);
134			break;
135		}
136		case GRADIENT_CIRCULAR: {
137		    agg::gradient_radial function;
138			_GenerateGradient(span, x, y, len, function, 0, 64, colors,
139				styleItem->transformation);
140			break;
141		}
142		case GRADIENT_DIAMOND: {
143		    agg::gradient_diamond function;
144			_GenerateGradient(span, x, y, len, function, 0, 64, colors,
145				styleItem->transformation);
146			break;
147		}
148		case GRADIENT_CONIC: {
149		    agg::gradient_conic function;
150			_GenerateGradient(span, x, y, len, function, 0, 64, colors,
151				styleItem->transformation);
152			break;
153		}
154		case GRADIENT_XY: {
155		    agg::gradient_xy function;
156			_GenerateGradient(span, x, y, len, function, 0, 64, colors,
157				styleItem->transformation);
158			break;
159		}
160		case GRADIENT_SQRT_XY: {
161		    agg::gradient_sqrt_xy function;
162			_GenerateGradient(span, x, y, len, function, 0, 64, colors,
163				styleItem->transformation);
164			break;
165		}
166	}
167}
168
169// _GenerateGradient
170template<class GradientFunction>
171void
172IconRenderer::StyleHandler::_GenerateGradient(agg::rgba8* span, int x, int y,
173	unsigned len, GradientFunction function, int32 start, int32 end,
174	const agg::rgba8* gradientColors, Transformation& gradientTransform)
175{
176	typedef agg::pod_auto_array<agg::rgba8, 256>	ColorArray;
177	typedef agg::span_interpolator_linear<>			Interpolator;
178	typedef agg::span_gradient<agg::rgba8,
179							   Interpolator,
180							   GradientFunction,
181							   ColorArray>			GradientGenerator;
182
183	Interpolator interpolator(gradientTransform);
184
185	ColorArray array(gradientColors);
186	GradientGenerator gradientGenerator(interpolator, function, array,
187		start, end);
188
189	gradientGenerator.generate(span, x, y, len);
190}
191
192// #pragma mark -
193
194class HintingTransformer {
195 public:
196
197	void transform(double* x, double* y) const
198	{
199		*x = floor(*x + 0.5);
200		*y = floor(*y + 0.5);
201	}
202};
203
204
205// #pragma mark -
206
207// constructor
208IconRenderer::IconRenderer(BBitmap* bitmap)
209	: fBitmap(bitmap),
210	  fBackground(NULL),
211	  fBackgroundColor(0, 0, 0, 0),
212	  fIcon(NULL),
213
214	  fGammaTable(2.2),
215
216	  fRenderingBuffer(),
217	  fPixelFormat(fRenderingBuffer),
218	  fPixelFormatPre(fRenderingBuffer),
219	  fBaseRenderer(fPixelFormat),
220	  fBaseRendererPre(fPixelFormatPre),
221
222	  fScanline(),
223	  fBinaryScanline(),
224	  fSpanAllocator(),
225
226	  fRasterizer(),
227
228	  fGlobalTransform()
229{
230	// attach rendering buffer to bitmap
231	fRenderingBuffer.attach((uint8*)bitmap->Bits(),
232		bitmap->Bounds().IntegerWidth() + 1,
233		bitmap->Bounds().IntegerHeight() + 1, bitmap->BytesPerRow());
234
235	fBaseRendererPre.clip_box(0, 0, bitmap->Bounds().IntegerWidth(),
236		bitmap->Bounds().IntegerHeight());
237}
238
239// destructor
240IconRenderer::~IconRenderer()
241{
242}
243
244// SetIcon
245void
246IconRenderer::SetIcon(const Icon* icon)
247{
248	if (fIcon == icon)
249		return;
250
251	fIcon = icon;
252	// TODO: ... ?
253}
254
255// Render
256void
257IconRenderer::Render()
258{
259	_Render(fBitmap->Bounds());
260}
261
262// Render
263void
264IconRenderer::Render(const BRect& area)
265{
266	_Render(fBitmap->Bounds() & area);
267}
268
269//SetScale
270void
271IconRenderer::SetScale(double scale)
272{
273	fGlobalTransform.reset();
274	fGlobalTransform.multiply(agg::trans_affine_scaling(scale));
275}
276
277//SetBackground
278void
279IconRenderer::SetBackground(const BBitmap* background)
280{
281	fBackground = background;
282}
283
284//SetBackground
285void
286IconRenderer::SetBackground(const agg::rgba8& background)
287{
288	fBackgroundColor.r = fGammaTable.dir(background.r);
289	fBackgroundColor.g = fGammaTable.dir(background.g);
290	fBackgroundColor.b = fGammaTable.dir(background.b);
291	fBackgroundColor.a = background.a;
292}
293
294// Demultiply
295void
296IconRenderer::Demultiply()
297{
298	uint8* bits = (uint8*)fBitmap->Bits();
299	uint32 bpr = fBitmap->BytesPerRow();
300	uint32 width = fBitmap->Bounds().IntegerWidth() + 1;
301	uint32 height = fBitmap->Bounds().IntegerHeight() + 1;
302
303	for (uint32 y = 0; y < height; y++) {
304		uint8* b = bits;
305		for (uint32 x = 0; x < width; x++) {
306			if (b[3] < 255 && b[3] > 0) {
307				b[0] = (uint8)((int)b[0] * 255 / b[3]);
308				b[1] = (uint8)((int)b[1] * 255 / b[3]);
309				b[2] = (uint8)((int)b[2] * 255 / b[3]);
310			}
311			b += 4;
312		}
313		bits += bpr;
314	}
315}
316
317// #pragma mark -
318
319typedef agg::conv_transform<VertexSource, Transformation> ScaledPath;
320typedef agg::conv_transform<ScaledPath, HintingTransformer> HintedPath;
321
322// _Render
323void
324IconRenderer::_Render(const BRect& r)
325{
326	if (!fIcon)
327		return;
328
329// TODO: fix clip box for "clear" and "apply_gamma_inv"
330//	fBaseRendererPre.clip_box((int)floorf(r.left), (int)floorf(r.top),
331//		(int)ceilf(r.right), (int)ceilf(r.bottom));
332
333	if (fBackground)
334		memcpy(fBitmap->Bits(), fBackground->Bits(), fBitmap->BitsLength());
335	else
336		fBaseRendererPre.clear(fBackgroundColor);
337
338//bigtime_t start = system_time();
339	StyleHandler styleHandler(fGammaTable);
340
341	fRasterizer.reset();
342	// iterate over the shapes in the icon,
343	// add the vector paths to the rasterizer
344	// and associate each shapes style
345	int32 shapeCount = fIcon->Shapes()->CountShapes();
346	int32 styleIndex = 0;
347	for (int32 i = 0; i < shapeCount; i++) {
348		Shape* shape = fIcon->Shapes()->ShapeAtFast(i);
349
350		// Don't render shape if the Level Of Detail falls out of range.
351		// That's unless the scale is bigger than the maximum
352		// MaxVisibilityScale of 4.0f.
353		if (fGlobalTransform.scale() < shape->MinVisibilityScale()
354			|| (fGlobalTransform.scale() > shape->MaxVisibilityScale()
355				&& shape->MaxVisibilityScale() < 4.0f)) {
356			continue;
357		}
358
359		Transformation transform(*shape);
360		transform.multiply(fGlobalTransform);
361			// NOTE: this works only because "agg::trans_affine",
362			// "Transformable" and "Transformation" are all the
363			// same thing
364
365		Style* style = shape->Style();
366		if (!style)
367			continue;
368
369		// add the style either with global transformation or with
370		// the shapes transformation, depending on whether there
371		// is a gradient and its settings
372		Gradient* gradient = style->Gradient();
373		bool styleAdded = false;
374		if (gradient && !gradient->InheritTransformation()) {
375			styleAdded = styleHandler.AddStyle(shape->Style(),
376				fGlobalTransform);
377		} else {
378			styleAdded = styleHandler.AddStyle(shape->Style(), transform);
379		}
380
381		if (!styleAdded) {
382			printf("IconRenderer::_Render() - out of memory\n");
383			break;
384		}
385
386		// if this is not the first shape, and the style contains
387		// transparency, commit a render pass of previous shapes
388		if (i > 0 && style->HasTransparency())
389			_CommitRenderPass(styleHandler);
390
391		fRasterizer.styles(styleIndex, -1);
392		styleIndex++;
393
394		// global scale
395		shape->SetGlobalScale(max_c(1.0, transform.scale()));
396		ScaledPath scaledPath(shape->VertexSource(), transform);
397		if (shape->Hinting()) {
398			// additional hinting
399			HintingTransformer hinter;
400			HintedPath hintedPath(scaledPath, hinter);
401			fRasterizer.add_path(hintedPath);
402		} else {
403			fRasterizer.add_path(scaledPath);
404		}
405	}
406
407	_CommitRenderPass(styleHandler, false);
408
409	if (fGammaTable.gamma() != 1.0)
410		fPixelFormat.apply_gamma_inv(fGammaTable);
411
412//if (fRenderingBuffer.width() == 64)
413//printf("rendering 64x64: %lld\n", system_time() - start);
414}
415
416// _CommitRenderPass
417void
418IconRenderer::_CommitRenderPass(StyleHandler& styleHandler, bool reset)
419{
420	agg::render_scanlines_compound(fRasterizer, fScanline, fBinaryScanline,
421		fBaseRendererPre, fSpanAllocator, styleHandler);
422
423	if (reset)
424		fRasterizer.reset();
425}
426
427
428
429
430