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