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