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
33using std::nothrow;
34
35class 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
92IconRenderer::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
100IconRenderer::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
109IconRenderer::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
120bool
121IconRenderer::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
140bool
141IconRenderer::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
159const agg::rgba8&
160IconRenderer::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
183void
184IconRenderer::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
251StyleTransformer*
252IconRenderer::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
293template<class GradientFunction>
294void
295IconRenderer::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
337void
338IconRenderer::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
383class HintingTransformer {
384 public:
385
386	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
397IconRenderer::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
429IconRenderer::~IconRenderer()
430{
431}
432
433
434void
435IconRenderer::SetIcon(const Icon* icon)
436{
437	if (fIcon == icon)
438		return;
439
440	fIcon = icon;
441	// TODO: ... ?
442}
443
444
445void
446#ifdef ICON_O_MATIC
447IconRenderer::Render(bool showReferences)
448#else
449IconRenderer::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
460void
461#ifdef ICON_O_MATIC
462IconRenderer::Render(const BRect& area, bool showReferences)
463#else
464IconRenderer::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
475void
476IconRenderer::SetScale(double scale)
477{
478	fGlobalTransform.reset();
479	fGlobalTransform.multiply(agg::trans_affine_scaling(scale));
480}
481
482
483void
484IconRenderer::SetBackground(const BBitmap* background)
485{
486	fBackground = background;
487}
488
489
490void
491IconRenderer::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
500void
501IconRenderer::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
526typedef agg::conv_transform<VertexSource, Transformable> ScaledPath;
527typedef agg::conv_transform<ScaledPath, HintingTransformer> HintedPath;
528
529
530void
531#ifdef ICON_O_MATIC
532IconRenderer::_Render(const BRect& r, bool showReferences)
533#else
534IconRenderer::_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
630void
631IconRenderer::_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