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