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