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 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 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 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 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 141 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& 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 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* 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 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 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 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 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 429 IconRenderer::~IconRenderer() 430 { 431 } 432 433 434 void 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 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 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 476 IconRenderer::SetScale(double scale) 477 { 478 fGlobalTransform.reset(); 479 fGlobalTransform.multiply(agg::trans_affine_scaling(scale)); 480 } 481 482 483 void 484 IconRenderer::SetBackground(const BBitmap* background) 485 { 486 fBackground = background; 487 } 488 489 490 void 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 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 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 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