1 /* 2 * Copyright 2006-2007, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 #include "IconRenderer.h" 10 11 #include <new> 12 #include <stdio.h> 13 14 #include <Bitmap.h> 15 #include <List.h> 16 17 #include <agg_span_gradient.h> 18 #include <agg_span_interpolator_linear.h> 19 20 #include "Gradient.h" 21 #include "Icon.h" 22 #include "Shape.h" 23 #include "ShapeContainer.h" 24 #include "Style.h" 25 #include "VectorPath.h" 26 27 using std::nothrow; 28 29 class IconRenderer::StyleHandler { 30 struct StyleItem { 31 Style* style; 32 Transformation transformation; 33 }; 34 35 public: 36 StyleHandler(::GammaTable& gammaTable) 37 : fStyles(20), 38 fGammaTable(gammaTable), 39 fTransparent(0, 0, 0, 0), 40 fColor(0, 0, 0, 0) 41 {} 42 43 ~StyleHandler() 44 { 45 int32 count = fStyles.CountItems(); 46 for (int32 i = 0; i < count; i++) 47 delete (StyleItem*)fStyles.ItemAtFast(i); 48 } 49 50 bool is_solid(unsigned styleIndex) const 51 { 52 StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex); 53 if (!styleItem) 54 return true; 55 56 return styleItem->style->Gradient() == NULL; 57 } 58 59 const agg::rgba8& color(unsigned styleIndex); 60 61 void generate_span(agg::rgba8* span, int x, int y, 62 unsigned len, unsigned styleIndex); 63 64 bool AddStyle(Style* style, const Transformation& transformation) 65 { 66 if (!style) 67 return false; 68 StyleItem* item = new (nothrow) StyleItem; 69 if (!item) 70 return false; 71 item->style = style; 72 // if the style uses a gradient, the transformation 73 // is based on the gradient transformation 74 if (Gradient* gradient = style->Gradient()) { 75 item->transformation = *gradient; 76 item->transformation.multiply(transformation); 77 } else { 78 item->transformation = transformation; 79 } 80 item->transformation.invert(); 81 return fStyles.AddItem((void*)item); 82 } 83 84 private: 85 template<class GradientFunction> 86 void _GenerateGradient(agg::rgba8* span, int x, int y, unsigned len, 87 GradientFunction function, int32 start, int32 end, 88 const agg::rgba8* gradientColors, 89 Transformation& gradientTransform); 90 91 BList fStyles; 92 ::GammaTable& fGammaTable; 93 agg::rgba8 fTransparent; 94 agg::rgba8 fColor; 95 }; 96 97 // color 98 const agg::rgba8& 99 IconRenderer::StyleHandler::color(unsigned styleIndex) 100 { 101 StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex); 102 if (!styleItem) { 103 printf("no style at index: %u!\n", styleIndex); 104 return fTransparent; 105 } 106 107 const rgb_color& c = styleItem->style->Color(); 108 fColor = agg::rgba8(fGammaTable.dir(c.red), 109 fGammaTable.dir(c.green), 110 fGammaTable.dir(c.blue), 111 c.alpha); 112 fColor.premultiply(); 113 return fColor; 114 } 115 116 // generate_span 117 void 118 IconRenderer::StyleHandler::generate_span(agg::rgba8* span, int x, int y, 119 unsigned len, unsigned styleIndex) 120 { 121 StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex); 122 if (!styleItem || !styleItem->style->Gradient()) { 123 printf("no style/gradient at index: %u!\n", styleIndex); 124 // TODO: memset() span? 125 return; 126 } 127 128 Style* style = styleItem->style; 129 Gradient* gradient = style->Gradient(); 130 const agg::rgba8* colors = style->GammaCorrectedColors(fGammaTable); 131 132 switch (gradient->Type()) { 133 case GRADIENT_LINEAR: { 134 agg::gradient_x function; 135 _GenerateGradient(span, x, y, len, function, -64, 64, colors, 136 styleItem->transformation); 137 break; 138 } 139 case GRADIENT_CIRCULAR: { 140 agg::gradient_radial function; 141 _GenerateGradient(span, x, y, len, function, 0, 64, colors, 142 styleItem->transformation); 143 break; 144 } 145 case GRADIENT_DIAMOND: { 146 agg::gradient_diamond function; 147 _GenerateGradient(span, x, y, len, function, 0, 64, colors, 148 styleItem->transformation); 149 break; 150 } 151 case GRADIENT_CONIC: { 152 agg::gradient_conic function; 153 _GenerateGradient(span, x, y, len, function, 0, 64, colors, 154 styleItem->transformation); 155 break; 156 } 157 case GRADIENT_XY: { 158 agg::gradient_xy function; 159 _GenerateGradient(span, x, y, len, function, 0, 64, colors, 160 styleItem->transformation); 161 break; 162 } 163 case GRADIENT_SQRT_XY: { 164 agg::gradient_sqrt_xy function; 165 _GenerateGradient(span, x, y, len, function, 0, 64, colors, 166 styleItem->transformation); 167 break; 168 } 169 } 170 } 171 172 // _GenerateGradient 173 template<class GradientFunction> 174 void 175 IconRenderer::StyleHandler::_GenerateGradient(agg::rgba8* span, int x, int y, 176 unsigned len, GradientFunction function, int32 start, int32 end, 177 const agg::rgba8* gradientColors, Transformation& gradientTransform) 178 { 179 typedef agg::pod_auto_array<agg::rgba8, 256> ColorArray; 180 typedef agg::span_interpolator_linear<> Interpolator; 181 typedef agg::span_gradient<agg::rgba8, 182 Interpolator, 183 GradientFunction, 184 ColorArray> GradientGenerator; 185 186 Interpolator interpolator(gradientTransform); 187 188 ColorArray array(gradientColors); 189 GradientGenerator gradientGenerator(interpolator, 190 function, 191 array, 192 start, end); 193 194 gradientGenerator.generate(span, x, y, len); 195 } 196 197 // #pragma mark - 198 199 class HintingTransformer { 200 public: 201 202 void transform(double* x, double* y) const 203 { 204 *x = floor(*x + 0.5); 205 *y = floor(*y + 0.5); 206 } 207 }; 208 209 210 // #pragma mark - 211 212 // constructor 213 IconRenderer::IconRenderer(BBitmap* bitmap) 214 : fBitmap(bitmap), 215 fBackground(NULL), 216 fBackgroundColor(0, 0, 0, 0), 217 fIcon(NULL), 218 219 fGammaTable(2.2), 220 221 fRenderingBuffer(), 222 fPixelFormat(fRenderingBuffer), 223 fPixelFormatPre(fRenderingBuffer), 224 fBaseRenderer(fPixelFormat), 225 fBaseRendererPre(fPixelFormatPre), 226 227 fScanline(), 228 fBinaryScanline(), 229 fSpanAllocator(), 230 231 fRasterizer(), 232 233 fGlobalTransform() 234 { 235 // attach rendering buffer to bitmap 236 fRenderingBuffer.attach((uint8*)bitmap->Bits(), 237 bitmap->Bounds().IntegerWidth() + 1, 238 bitmap->Bounds().IntegerHeight() + 1, 239 bitmap->BytesPerRow()); 240 241 fBaseRendererPre.clip_box(0, 242 0, 243 fBitmap->Bounds().IntegerWidth(), 244 fBitmap->Bounds().IntegerHeight()); 245 } 246 247 // destructor 248 IconRenderer::~IconRenderer() 249 { 250 } 251 252 // SetIcon 253 void 254 IconRenderer::SetIcon(const Icon* icon) 255 { 256 if (fIcon == icon) 257 return; 258 259 fIcon = icon; 260 // TODO: ... ? 261 } 262 263 // Render 264 void 265 IconRenderer::Render() 266 { 267 _Render(fBitmap->Bounds()); 268 } 269 270 // Render 271 void 272 IconRenderer::Render(const BRect& area) 273 { 274 _Render(fBitmap->Bounds() & area); 275 } 276 277 //SetScale 278 void 279 IconRenderer::SetScale(double scale) 280 { 281 fGlobalTransform.reset(); 282 fGlobalTransform.multiply(agg::trans_affine_scaling(scale)); 283 } 284 285 //SetBackground 286 void 287 IconRenderer::SetBackground(const BBitmap* background) 288 { 289 fBackground = background; 290 } 291 292 //SetBackground 293 void 294 IconRenderer::SetBackground(const agg::rgba8& background) 295 { 296 fBackgroundColor.r = fGammaTable.dir(background.r); 297 fBackgroundColor.g = fGammaTable.dir(background.g); 298 fBackgroundColor.b = fGammaTable.dir(background.b); 299 fBackgroundColor.a = background.a; 300 } 301 302 // Demultiply 303 void 304 IconRenderer::Demultiply() 305 { 306 uint8* bits = (uint8*)fBitmap->Bits(); 307 uint32 bpr = fBitmap->BytesPerRow(); 308 uint32 width = fBitmap->Bounds().IntegerWidth() + 1; 309 uint32 height = fBitmap->Bounds().IntegerHeight() + 1; 310 311 for (uint32 y = 0; y < height; y++) { 312 uint8* b = bits; 313 for (uint32 x = 0; x < width; x++) { 314 if (b[3] < 255 && b[3] > 0) { 315 b[0] = (uint8)((int)b[0] * 255 / b[3]); 316 b[1] = (uint8)((int)b[1] * 255 / b[3]); 317 b[2] = (uint8)((int)b[2] * 255 / b[3]); 318 } 319 b += 4; 320 } 321 bits += bpr; 322 } 323 } 324 325 // #pragma mark - 326 327 typedef agg::conv_transform<VertexSource, Transformation> ScaledPath; 328 typedef agg::conv_transform<ScaledPath, HintingTransformer> HintedPath; 329 330 // _Render 331 void 332 IconRenderer::_Render(const BRect& r) 333 { 334 if (!fIcon) 335 return; 336 337 // TODO: fix clip box for "clear" and "apply_gamma_inv" 338 // fBaseRendererPre.clip_box((int)floorf(r.left), 339 // (int)floorf(r.top), 340 // (int)ceilf(r.right), 341 // (int)ceilf(r.bottom)); 342 343 if (fBackground) 344 memcpy(fBitmap->Bits(), fBackground->Bits(), fBitmap->BitsLength()); 345 else 346 fBaseRendererPre.clear(fBackgroundColor); 347 348 //bigtime_t start = system_time(); 349 StyleHandler styleHandler(fGammaTable); 350 351 fRasterizer.reset(); 352 // iterate over the shapes in the icon, 353 // add the vector paths to the rasterizer 354 // and associate each shapes style 355 int32 shapeCount = fIcon->Shapes()->CountShapes(); 356 int32 styleIndex = 0; 357 for (int32 i = 0; i < shapeCount; i++) { 358 Shape* shape = fIcon->Shapes()->ShapeAtFast(i); 359 360 Transformation transform(*shape); 361 transform.multiply(fGlobalTransform); 362 // NOTE: this works only because "agg::trans_affine", 363 // "Transformable" and "Transformation" are all the 364 // same thing 365 366 // don't render shape if the Level Of Detail falls 367 // out of range 368 if (transform.scale() <= shape->MinVisibilityScale() 369 || transform.scale() > shape->MaxVisibilityScale()) 370 continue; 371 372 Style* style = shape->Style(); 373 if (!style) 374 continue; 375 376 // add the style either with global transformation or with 377 // the shapes transformation, depending on wether there 378 // is a gradient and its settings 379 Gradient* gradient = style->Gradient(); 380 bool styleAdded = false; 381 if (gradient && !gradient->InheritTransformation()) { 382 styleAdded = styleHandler.AddStyle(shape->Style(), 383 fGlobalTransform); 384 } else { 385 styleAdded = styleHandler.AddStyle(shape->Style(), 386 transform); 387 } 388 389 if (!styleAdded) { 390 printf("IconRenderer::_Render() - out of memory\n"); 391 break; 392 } 393 394 // if this is not the first shape, and the style contains 395 // transparency, commit a render pass of previous shapes 396 if (i > 0 && style->HasTransparency()) 397 _CommitRenderPass(styleHandler); 398 399 fRasterizer.styles(styleIndex, -1); 400 styleIndex++; 401 402 // global scale 403 shape->SetGlobalScale(max_c(1.0, transform.scale())); 404 ScaledPath scaledPath(shape->VertexSource(), transform); 405 if (shape->Hinting()) { 406 // additional hinting 407 HintingTransformer hinter; 408 HintedPath hintedPath(scaledPath, hinter); 409 fRasterizer.add_path(hintedPath); 410 } else { 411 fRasterizer.add_path(scaledPath); 412 } 413 } 414 415 _CommitRenderPass(styleHandler, false); 416 417 if (fGammaTable.gamma() != 1.0) 418 fPixelFormat.apply_gamma_inv(fGammaTable); 419 420 //if (fRenderingBuffer.width() == 64) 421 //printf("rendering 64x64: %lld\n", system_time() - start); 422 } 423 424 // _CommitRenderPass 425 void 426 IconRenderer::_CommitRenderPass(StyleHandler& styleHandler, bool reset) 427 { 428 agg::render_scanlines_compound(fRasterizer, 429 fScanline, 430 fBinaryScanline, 431 fBaseRendererPre, 432 fSpanAllocator, 433 styleHandler); 434 if (reset) 435 fRasterizer.reset(); 436 } 437 438 439 440 441