xref: /haiku/src/libs/icon/IconRenderer.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 		// 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
417 void
418 IconRenderer::_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