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