xref: /haiku/src/libs/icon/IconRenderer.cpp (revision 508f54795f39c3e7552d87c95aae9dd8ec6f505b)
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 
27 using std::nothrow;
28 
29 class 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 
84 private:
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
97 const agg::rgba8&
98 IconRenderer::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
114 void
115 IconRenderer::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
170 template<class GradientFunction>
171 void
172 IconRenderer::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 
194 class 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
208 IconRenderer::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
240 IconRenderer::~IconRenderer()
241 {
242 }
243 
244 // SetIcon
245 void
246 IconRenderer::SetIcon(const Icon* icon)
247 {
248 	if (fIcon == icon)
249 		return;
250 
251 	fIcon = icon;
252 	// TODO: ... ?
253 }
254 
255 // Render
256 void
257 IconRenderer::Render()
258 {
259 	_Render(fBitmap->Bounds());
260 }
261 
262 // Render
263 void
264 IconRenderer::Render(const BRect& area)
265 {
266 	_Render(fBitmap->Bounds() & area);
267 }
268 
269 //SetScale
270 void
271 IconRenderer::SetScale(double scale)
272 {
273 	fGlobalTransform.reset();
274 	fGlobalTransform.multiply(agg::trans_affine_scaling(scale));
275 }
276 
277 //SetBackground
278 void
279 IconRenderer::SetBackground(const BBitmap* background)
280 {
281 	fBackground = background;
282 }
283 
284 //SetBackground
285 void
286 IconRenderer::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
295 void
296 IconRenderer::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 
319 typedef agg::conv_transform<VertexSource, Transformation> ScaledPath;
320 typedef agg::conv_transform<ScaledPath, HintingTransformer> HintedPath;
321 
322 // _Render
323 void
324 IconRenderer::_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 		if (fGlobalTransform.scale() < shape->MinVisibilityScale()
352 			|| fGlobalTransform.scale() > shape->MaxVisibilityScale())
353 			continue;
354 
355 		Transformation transform(*shape);
356 		transform.multiply(fGlobalTransform);
357 			// NOTE: this works only because "agg::trans_affine",
358 			// "Transformable" and "Transformation" are all the
359 			// same thing
360 
361 		Style* style = shape->Style();
362 		if (!style)
363 			continue;
364 
365 		// add the style either with global transformation or with
366 		// the shapes transformation, depending on wether there
367 		// is a gradient and its settings
368 		Gradient* gradient = style->Gradient();
369 		bool styleAdded = false;
370 		if (gradient && !gradient->InheritTransformation()) {
371 			styleAdded = styleHandler.AddStyle(shape->Style(),
372 				fGlobalTransform);
373 		} else {
374 			styleAdded = styleHandler.AddStyle(shape->Style(), transform);
375 		}
376 
377 		if (!styleAdded) {
378 			printf("IconRenderer::_Render() - out of memory\n");
379 			break;
380 		}
381 
382 		// if this is not the first shape, and the style contains
383 		// transparency, commit a render pass of previous shapes
384 		if (i > 0 && style->HasTransparency())
385 			_CommitRenderPass(styleHandler);
386 
387 		fRasterizer.styles(styleIndex, -1);
388 		styleIndex++;
389 
390 		// global scale
391 		shape->SetGlobalScale(max_c(1.0, transform.scale()));
392 		ScaledPath scaledPath(shape->VertexSource(), transform);
393 		if (shape->Hinting()) {
394 			// additional hinting
395 			HintingTransformer hinter;
396 			HintedPath hintedPath(scaledPath, hinter);
397 			fRasterizer.add_path(hintedPath);
398 		} else {
399 			fRasterizer.add_path(scaledPath);
400 		}
401 	}
402 
403 	_CommitRenderPass(styleHandler, false);
404 
405 	if (fGammaTable.gamma() != 1.0)
406 		fPixelFormat.apply_gamma_inv(fGammaTable);
407 
408 //if (fRenderingBuffer.width() == 64)
409 //printf("rendering 64x64: %lld\n", system_time() - start);
410 }
411 
412 // _CommitRenderPass
413 void
414 IconRenderer::_CommitRenderPass(StyleHandler& styleHandler, bool reset)
415 {
416 	agg::render_scanlines_compound(fRasterizer, fScanline, fBinaryScanline,
417 		fBaseRendererPre, fSpanAllocator, styleHandler);
418 
419 	if (reset)
420 		fRasterizer.reset();
421 }
422 
423 
424 
425 
426