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