xref: /haiku/src/libs/icon/IconRenderer.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
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 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 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 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 StyleHandler::_GenerateGradient(agg::rgba8* span, int x, int y, unsigned len,
176 								GradientFunction function,
177 								int32 start, int32 end,
178 								const agg::rgba8* gradientColors,
179 								Transformation& gradientTransform)
180 
181 {
182 	typedef agg::pod_auto_array<agg::rgba8, 256>	ColorArray;
183 	typedef agg::span_interpolator_linear<>			Interpolator;
184 	typedef agg::span_gradient<agg::rgba8,
185 							   Interpolator,
186 							   GradientFunction,
187 							   ColorArray>			GradientGenerator;
188 
189 	Interpolator interpolator(gradientTransform);
190 
191 	ColorArray array(gradientColors);
192 	GradientGenerator gradientGenerator(interpolator,
193 										function,
194 										array,
195 										start, end);
196 
197 	gradientGenerator.generate(span, x, y, len);
198 }
199 
200 // #pragma mark -
201 
202 class HintingTransformer {
203  public:
204 
205 	void transform(double* x, double* y) const
206 	{
207 		*x = floor(*x + 0.5);
208 		*y = floor(*y + 0.5);
209 	}
210 };
211 
212 
213 // #pragma mark -
214 
215 // constructor
216 IconRenderer::IconRenderer(BBitmap* bitmap)
217 	: fBitmap(bitmap),
218 	  fBackground(NULL),
219 	  fBackgroundColor(0, 0, 0, 0),
220 	  fIcon(NULL),
221 
222 	  fGammaTable(2.2),
223 
224 	  fRenderingBuffer(),
225 	  fPixelFormat(fRenderingBuffer),
226 	  fPixelFormatPre(fRenderingBuffer),
227 	  fBaseRenderer(fPixelFormat),
228 	  fBaseRendererPre(fPixelFormatPre),
229 
230 	  fScanline(),
231 	  fBinaryScanline(),
232 	  fSpanAllocator(),
233 
234 	  fRasterizer(),
235 
236 	  fGlobalTransform()
237 {
238 	// attach rendering buffer to bitmap
239 	fRenderingBuffer.attach((uint8*)bitmap->Bits(),
240 							bitmap->Bounds().IntegerWidth() + 1,
241 							bitmap->Bounds().IntegerHeight() + 1,
242 							bitmap->BytesPerRow());
243 
244 	fBaseRendererPre.clip_box(0,
245 							  0,
246 							  fBitmap->Bounds().IntegerWidth(),
247 							  fBitmap->Bounds().IntegerHeight());
248 }
249 
250 // destructor
251 IconRenderer::~IconRenderer()
252 {
253 }
254 
255 // SetIcon
256 void
257 IconRenderer::SetIcon(const Icon* icon)
258 {
259 	if (fIcon == icon)
260 		return;
261 
262 	fIcon = icon;
263 	// TODO: ... ?
264 }
265 
266 // Render
267 void
268 IconRenderer::Render()
269 {
270 	_Render(fBitmap->Bounds());
271 }
272 
273 // Render
274 void
275 IconRenderer::Render(const BRect& area)
276 {
277 	_Render(fBitmap->Bounds() & area);
278 }
279 
280 //SetScale
281 void
282 IconRenderer::SetScale(double scale)
283 {
284 	fGlobalTransform.reset();
285 	fGlobalTransform.multiply(agg::trans_affine_scaling(scale));
286 }
287 
288 //SetBackground
289 void
290 IconRenderer::SetBackground(const BBitmap* background)
291 {
292 	fBackground = background;
293 }
294 
295 //SetBackground
296 void
297 IconRenderer::SetBackground(const agg::rgba8& background)
298 {
299 	fBackgroundColor.r = fGammaTable.dir(background.r);
300 	fBackgroundColor.g = fGammaTable.dir(background.g);
301 	fBackgroundColor.b = fGammaTable.dir(background.b);
302 	fBackgroundColor.a = background.a;
303 }
304 
305 // Demultiply
306 void
307 IconRenderer::Demultiply()
308 {
309 	uint8* bits = (uint8*)fBitmap->Bits();
310 	uint32 bpr = fBitmap->BytesPerRow();
311 	uint32 width = fBitmap->Bounds().IntegerWidth() + 1;
312 	uint32 height = fBitmap->Bounds().IntegerHeight() + 1;
313 
314 	for (uint32 y = 0; y < height; y++) {
315 		uint8* b = bits;
316 		for (uint32 x = 0; x < width; x++) {
317 			if (b[3] < 255 && b[3] > 0) {
318 				b[0] = (uint8)((int)b[0] * 255 / b[3]);
319 				b[1] = (uint8)((int)b[1] * 255 / b[3]);
320 				b[2] = (uint8)((int)b[2] * 255 / b[3]);
321 			}
322 			b += 4;
323 		}
324 		bits += bpr;
325 	}
326 }
327 
328 // #pragma mark -
329 
330 typedef agg::conv_transform<VertexSource, Transformation> ScaledPath;
331 typedef agg::conv_transform<ScaledPath, HintingTransformer> HintedPath;
332 
333 // _Render
334 void
335 IconRenderer::_Render(const BRect& r)
336 {
337 	if (!fIcon)
338 		return;
339 
340 // TODO: fix clip box for "clear" and "apply_gamma_inv"
341 //	fBaseRendererPre.clip_box((int)floorf(r.left),
342 //							  (int)floorf(r.top),
343 //							  (int)ceilf(r.right),
344 //							  (int)ceilf(r.bottom));
345 
346 	if (fBackground)
347 		memcpy(fBitmap->Bits(), fBackground->Bits(), fBitmap->BitsLength());
348 	else
349 		fBaseRendererPre.clear(fBackgroundColor);
350 
351 //bigtime_t start = system_time();
352 	StyleHandler styleHandler(fGammaTable);
353 
354 	fRasterizer.reset();
355 	// iterate over the shapes in the icon,
356 	// add the vector paths to the rasterizer
357 	// and associate each shapes style
358 	int32 shapeCount = fIcon->Shapes()->CountShapes();
359 	int32 styleIndex = 0;
360 	for (int32 i = 0; i < shapeCount; i++) {
361 		Shape* shape = fIcon->Shapes()->ShapeAtFast(i);
362 
363 		Transformation transform(*shape);
364 		transform.multiply(fGlobalTransform);
365 			// NOTE: this works only because "agg::trans_affine",
366 			// "Transformable" and "Transformation" are all the
367 			// same thing
368 
369 		// don't render shape if the Level Of Detail falls
370 		// out of range
371 		if (transform.scale() <= shape->MinVisibilityScale()
372 			|| transform.scale() > shape->MaxVisibilityScale())
373 			continue;
374 
375 		Style* style = shape->Style();
376 		if (!style)
377 			continue;
378 
379 		// add the style either with global transformation or with
380 		// the shapes transformation, depending on wether there
381 		// is a gradient and its settings
382 		Gradient* gradient = style->Gradient();
383 		bool styleAdded = false;
384 		if (gradient && !gradient->InheritTransformation()) {
385 			styleAdded = styleHandler.AddStyle(shape->Style(),
386 											   fGlobalTransform);
387 		} else {
388 			styleAdded = styleHandler.AddStyle(shape->Style(),
389 											   transform);
390 		}
391 
392 		if (!styleAdded) {
393 			printf("IconRenderer::_Render() - out of memory\n");
394 			break;
395 		}
396 		fRasterizer.styles(styleIndex, -1);
397 		styleIndex++;
398 
399 		// global scale
400 		shape->SetGlobalScale(max_c(1.0, transform.scale()));
401 		ScaledPath scaledPath(shape->VertexSource(), transform);
402 		if (shape->Hinting()) {
403 			// additional hinting
404 			HintingTransformer hinter;
405 			HintedPath hintedPath(scaledPath, hinter);
406 			fRasterizer.add_path(hintedPath);
407 		} else {
408 			fRasterizer.add_path(scaledPath);
409 		}
410 	}
411 
412 	agg::render_scanlines_compound(fRasterizer,
413 								   fScanline,
414 								   fBinaryScanline,
415 								   fBaseRendererPre,
416 								   fSpanAllocator,
417 								   styleHandler);
418 
419 	if (fGammaTable.gamma() != 1.0)
420 		fPixelFormat.apply_gamma_inv(fGammaTable);
421 
422 //if (fRenderingBuffer.width() == 64)
423 //printf("rendering 64x64: %lld\n", system_time() - start);
424 }
425 
426 
427 
428 
429 
430