xref: /haiku/src/libs/icon/IconRenderer.cpp (revision cfc3fa87da824bdf593eb8b817a83b6376e77935)
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 "Gradient.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,
89 						   Transformation& gradientTransform);
90 
91 	BList				fStyles;
92 	::GammaTable&		fGammaTable;
93 	agg::rgba8			fTransparent;
94 	agg::rgba8			fColor;
95 };
96 
97 // color
98 const agg::rgba8&
99 IconRenderer::StyleHandler::color(unsigned styleIndex)
100 {
101 	StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex);
102 	if (!styleItem) {
103 		printf("no style at index: %u!\n", styleIndex);
104 		return fTransparent;
105 	}
106 
107 	const rgb_color& c = styleItem->style->Color();
108 	fColor = agg::rgba8(fGammaTable.dir(c.red),
109 						fGammaTable.dir(c.green),
110 						fGammaTable.dir(c.blue),
111 						c.alpha);
112 	fColor.premultiply();
113     return fColor;
114 }
115 
116 // generate_span
117 void
118 IconRenderer::StyleHandler::generate_span(agg::rgba8* span, int x, int y,
119 	unsigned len, unsigned styleIndex)
120 {
121 	StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex);
122 	if (!styleItem || !styleItem->style->Gradient()) {
123 		printf("no style/gradient at index: %u!\n", styleIndex);
124 		// TODO: memset() span?
125 		return;
126 	}
127 
128 	Style* style = styleItem->style;
129 	Gradient* gradient = style->Gradient();
130 	const agg::rgba8* colors = style->GammaCorrectedColors(fGammaTable);
131 
132 	switch (gradient->Type()) {
133 		case GRADIENT_LINEAR: {
134 		    agg::gradient_x function;
135 			_GenerateGradient(span, x, y, len, function, -64, 64, colors,
136 							  styleItem->transformation);
137 			break;
138 		}
139 		case GRADIENT_CIRCULAR: {
140 		    agg::gradient_radial function;
141 			_GenerateGradient(span, x, y, len, function, 0, 64, colors,
142 							  styleItem->transformation);
143 			break;
144 		}
145 		case GRADIENT_DIAMOND: {
146 		    agg::gradient_diamond function;
147 			_GenerateGradient(span, x, y, len, function, 0, 64, colors,
148 							  styleItem->transformation);
149 			break;
150 		}
151 		case GRADIENT_CONIC: {
152 		    agg::gradient_conic function;
153 			_GenerateGradient(span, x, y, len, function, 0, 64, colors,
154 							  styleItem->transformation);
155 			break;
156 		}
157 		case GRADIENT_XY: {
158 		    agg::gradient_xy function;
159 			_GenerateGradient(span, x, y, len, function, 0, 64, colors,
160 							  styleItem->transformation);
161 			break;
162 		}
163 		case GRADIENT_SQRT_XY: {
164 		    agg::gradient_sqrt_xy function;
165 			_GenerateGradient(span, x, y, len, function, 0, 64, colors,
166 							  styleItem->transformation);
167 			break;
168 		}
169 	}
170 }
171 
172 // _GenerateGradient
173 template<class GradientFunction>
174 void
175 IconRenderer::StyleHandler::_GenerateGradient(agg::rgba8* span, int x, int y,
176 	unsigned len, GradientFunction function, int32 start, int32 end,
177 	const agg::rgba8* gradientColors, Transformation& gradientTransform)
178 {
179 	typedef agg::pod_auto_array<agg::rgba8, 256>	ColorArray;
180 	typedef agg::span_interpolator_linear<>			Interpolator;
181 	typedef agg::span_gradient<agg::rgba8,
182 							   Interpolator,
183 							   GradientFunction,
184 							   ColorArray>			GradientGenerator;
185 
186 	Interpolator interpolator(gradientTransform);
187 
188 	ColorArray array(gradientColors);
189 	GradientGenerator gradientGenerator(interpolator,
190 										function,
191 										array,
192 										start, end);
193 
194 	gradientGenerator.generate(span, x, y, len);
195 }
196 
197 // #pragma mark -
198 
199 class HintingTransformer {
200  public:
201 
202 	void transform(double* x, double* y) const
203 	{
204 		*x = floor(*x + 0.5);
205 		*y = floor(*y + 0.5);
206 	}
207 };
208 
209 
210 // #pragma mark -
211 
212 // constructor
213 IconRenderer::IconRenderer(BBitmap* bitmap)
214 	: fBitmap(bitmap),
215 	  fBackground(NULL),
216 	  fBackgroundColor(0, 0, 0, 0),
217 	  fIcon(NULL),
218 
219 	  fGammaTable(2.2),
220 
221 	  fRenderingBuffer(),
222 	  fPixelFormat(fRenderingBuffer),
223 	  fPixelFormatPre(fRenderingBuffer),
224 	  fBaseRenderer(fPixelFormat),
225 	  fBaseRendererPre(fPixelFormatPre),
226 
227 	  fScanline(),
228 	  fBinaryScanline(),
229 	  fSpanAllocator(),
230 
231 	  fRasterizer(),
232 
233 	  fGlobalTransform()
234 {
235 	// attach rendering buffer to bitmap
236 	fRenderingBuffer.attach((uint8*)bitmap->Bits(),
237 							bitmap->Bounds().IntegerWidth() + 1,
238 							bitmap->Bounds().IntegerHeight() + 1,
239 							bitmap->BytesPerRow());
240 
241 	fBaseRendererPre.clip_box(0,
242 							  0,
243 							  fBitmap->Bounds().IntegerWidth(),
244 							  fBitmap->Bounds().IntegerHeight());
245 }
246 
247 // destructor
248 IconRenderer::~IconRenderer()
249 {
250 }
251 
252 // SetIcon
253 void
254 IconRenderer::SetIcon(const Icon* icon)
255 {
256 	if (fIcon == icon)
257 		return;
258 
259 	fIcon = icon;
260 	// TODO: ... ?
261 }
262 
263 // Render
264 void
265 IconRenderer::Render()
266 {
267 	_Render(fBitmap->Bounds());
268 }
269 
270 // Render
271 void
272 IconRenderer::Render(const BRect& area)
273 {
274 	_Render(fBitmap->Bounds() & area);
275 }
276 
277 //SetScale
278 void
279 IconRenderer::SetScale(double scale)
280 {
281 	fGlobalTransform.reset();
282 	fGlobalTransform.multiply(agg::trans_affine_scaling(scale));
283 }
284 
285 //SetBackground
286 void
287 IconRenderer::SetBackground(const BBitmap* background)
288 {
289 	fBackground = background;
290 }
291 
292 //SetBackground
293 void
294 IconRenderer::SetBackground(const agg::rgba8& background)
295 {
296 	fBackgroundColor.r = fGammaTable.dir(background.r);
297 	fBackgroundColor.g = fGammaTable.dir(background.g);
298 	fBackgroundColor.b = fGammaTable.dir(background.b);
299 	fBackgroundColor.a = background.a;
300 }
301 
302 // Demultiply
303 void
304 IconRenderer::Demultiply()
305 {
306 	uint8* bits = (uint8*)fBitmap->Bits();
307 	uint32 bpr = fBitmap->BytesPerRow();
308 	uint32 width = fBitmap->Bounds().IntegerWidth() + 1;
309 	uint32 height = fBitmap->Bounds().IntegerHeight() + 1;
310 
311 	for (uint32 y = 0; y < height; y++) {
312 		uint8* b = bits;
313 		for (uint32 x = 0; x < width; x++) {
314 			if (b[3] < 255 && b[3] > 0) {
315 				b[0] = (uint8)((int)b[0] * 255 / b[3]);
316 				b[1] = (uint8)((int)b[1] * 255 / b[3]);
317 				b[2] = (uint8)((int)b[2] * 255 / b[3]);
318 			}
319 			b += 4;
320 		}
321 		bits += bpr;
322 	}
323 }
324 
325 // #pragma mark -
326 
327 typedef agg::conv_transform<VertexSource, Transformation> ScaledPath;
328 typedef agg::conv_transform<ScaledPath, HintingTransformer> HintedPath;
329 
330 // _Render
331 void
332 IconRenderer::_Render(const BRect& r)
333 {
334 	if (!fIcon)
335 		return;
336 
337 // TODO: fix clip box for "clear" and "apply_gamma_inv"
338 //	fBaseRendererPre.clip_box((int)floorf(r.left),
339 //							  (int)floorf(r.top),
340 //							  (int)ceilf(r.right),
341 //							  (int)ceilf(r.bottom));
342 
343 	if (fBackground)
344 		memcpy(fBitmap->Bits(), fBackground->Bits(), fBitmap->BitsLength());
345 	else
346 		fBaseRendererPre.clear(fBackgroundColor);
347 
348 //bigtime_t start = system_time();
349 	StyleHandler styleHandler(fGammaTable);
350 
351 	fRasterizer.reset();
352 	// iterate over the shapes in the icon,
353 	// add the vector paths to the rasterizer
354 	// and associate each shapes style
355 	int32 shapeCount = fIcon->Shapes()->CountShapes();
356 	int32 styleIndex = 0;
357 	for (int32 i = 0; i < shapeCount; i++) {
358 		Shape* shape = fIcon->Shapes()->ShapeAtFast(i);
359 
360 		Transformation transform(*shape);
361 		transform.multiply(fGlobalTransform);
362 			// NOTE: this works only because "agg::trans_affine",
363 			// "Transformable" and "Transformation" are all the
364 			// same thing
365 
366 		// don't render shape if the Level Of Detail falls
367 		// out of range
368 		if (transform.scale() <= shape->MinVisibilityScale()
369 			|| transform.scale() > shape->MaxVisibilityScale())
370 			continue;
371 
372 		Style* style = shape->Style();
373 		if (!style)
374 			continue;
375 
376 		// add the style either with global transformation or with
377 		// the shapes transformation, depending on wether there
378 		// is a gradient and its settings
379 		Gradient* gradient = style->Gradient();
380 		bool styleAdded = false;
381 		if (gradient && !gradient->InheritTransformation()) {
382 			styleAdded = styleHandler.AddStyle(shape->Style(),
383 											   fGlobalTransform);
384 		} else {
385 			styleAdded = styleHandler.AddStyle(shape->Style(),
386 											   transform);
387 		}
388 
389 		if (!styleAdded) {
390 			printf("IconRenderer::_Render() - out of memory\n");
391 			break;
392 		}
393 
394 		// if this is not the first shape, and the style contains
395 		// transparency, commit a render pass of previous shapes
396 		if (i > 0 && style->HasTransparency())
397 			_CommitRenderPass(styleHandler);
398 
399 		fRasterizer.styles(styleIndex, -1);
400 		styleIndex++;
401 
402 		// global scale
403 		shape->SetGlobalScale(max_c(1.0, transform.scale()));
404 		ScaledPath scaledPath(shape->VertexSource(), transform);
405 		if (shape->Hinting()) {
406 			// additional hinting
407 			HintingTransformer hinter;
408 			HintedPath hintedPath(scaledPath, hinter);
409 			fRasterizer.add_path(hintedPath);
410 		} else {
411 			fRasterizer.add_path(scaledPath);
412 		}
413 	}
414 
415 	_CommitRenderPass(styleHandler, false);
416 
417 	if (fGammaTable.gamma() != 1.0)
418 		fPixelFormat.apply_gamma_inv(fGammaTable);
419 
420 //if (fRenderingBuffer.width() == 64)
421 //printf("rendering 64x64: %lld\n", system_time() - start);
422 }
423 
424 // _CommitRenderPass
425 void
426 IconRenderer::_CommitRenderPass(StyleHandler& styleHandler, bool reset)
427 {
428 	agg::render_scanlines_compound(fRasterizer,
429 								   fScanline,
430 								   fBinaryScanline,
431 								   fBaseRendererPre,
432 								   fSpanAllocator,
433 								   styleHandler);
434 	if (reset)
435 		fRasterizer.reset();
436 }
437 
438 
439 
440 
441