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