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 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 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 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 StyleHandler::_GenerateGradient(agg::rgba8* span, int x, int y, unsigned len, 176 GradientFunction function, 177 int32 start, int32 end, 178 const agg::rgba8* gradientColors, 179 Transformation& gradientTransform) 180 181 { 182 typedef agg::pod_auto_array<agg::rgba8, 256> ColorArray; 183 typedef agg::span_interpolator_linear<> Interpolator; 184 typedef agg::span_gradient<agg::rgba8, 185 Interpolator, 186 GradientFunction, 187 ColorArray> GradientGenerator; 188 189 Interpolator interpolator(gradientTransform); 190 191 ColorArray array(gradientColors); 192 GradientGenerator gradientGenerator(interpolator, 193 function, 194 array, 195 start, end); 196 197 gradientGenerator.generate(span, x, y, len); 198 } 199 200 // #pragma mark - 201 202 class HintingTransformer { 203 public: 204 205 void transform(double* x, double* y) const 206 { 207 *x = floor(*x + 0.5); 208 *y = floor(*y + 0.5); 209 } 210 }; 211 212 213 // #pragma mark - 214 215 // constructor 216 IconRenderer::IconRenderer(BBitmap* bitmap) 217 : fBitmap(bitmap), 218 fBackground(NULL), 219 fBackgroundColor(0, 0, 0, 0), 220 fIcon(NULL), 221 222 fGammaTable(2.2), 223 224 fRenderingBuffer(), 225 fPixelFormat(fRenderingBuffer), 226 fPixelFormatPre(fRenderingBuffer), 227 fBaseRenderer(fPixelFormat), 228 fBaseRendererPre(fPixelFormatPre), 229 230 fScanline(), 231 fBinaryScanline(), 232 fSpanAllocator(), 233 234 fRasterizer(), 235 236 fGlobalTransform() 237 { 238 // attach rendering buffer to bitmap 239 fRenderingBuffer.attach((uint8*)bitmap->Bits(), 240 bitmap->Bounds().IntegerWidth() + 1, 241 bitmap->Bounds().IntegerHeight() + 1, 242 bitmap->BytesPerRow()); 243 244 fBaseRendererPre.clip_box(0, 245 0, 246 fBitmap->Bounds().IntegerWidth(), 247 fBitmap->Bounds().IntegerHeight()); 248 } 249 250 // destructor 251 IconRenderer::~IconRenderer() 252 { 253 } 254 255 // SetIcon 256 void 257 IconRenderer::SetIcon(const Icon* icon) 258 { 259 if (fIcon == icon) 260 return; 261 262 fIcon = icon; 263 // TODO: ... ? 264 } 265 266 // Render 267 void 268 IconRenderer::Render() 269 { 270 _Render(fBitmap->Bounds()); 271 } 272 273 // Render 274 void 275 IconRenderer::Render(const BRect& area) 276 { 277 _Render(fBitmap->Bounds() & area); 278 } 279 280 //SetScale 281 void 282 IconRenderer::SetScale(double scale) 283 { 284 fGlobalTransform.reset(); 285 fGlobalTransform.multiply(agg::trans_affine_scaling(scale)); 286 } 287 288 //SetBackground 289 void 290 IconRenderer::SetBackground(const BBitmap* background) 291 { 292 fBackground = background; 293 } 294 295 //SetBackground 296 void 297 IconRenderer::SetBackground(const agg::rgba8& background) 298 { 299 fBackgroundColor.r = fGammaTable.dir(background.r); 300 fBackgroundColor.g = fGammaTable.dir(background.g); 301 fBackgroundColor.b = fGammaTable.dir(background.b); 302 fBackgroundColor.a = background.a; 303 } 304 305 // Demultiply 306 void 307 IconRenderer::Demultiply() 308 { 309 uint8* bits = (uint8*)fBitmap->Bits(); 310 uint32 bpr = fBitmap->BytesPerRow(); 311 uint32 width = fBitmap->Bounds().IntegerWidth() + 1; 312 uint32 height = fBitmap->Bounds().IntegerHeight() + 1; 313 314 for (uint32 y = 0; y < height; y++) { 315 uint8* b = bits; 316 for (uint32 x = 0; x < width; x++) { 317 if (b[3] < 255 && b[3] > 0) { 318 b[0] = (uint8)((int)b[0] * 255 / b[3]); 319 b[1] = (uint8)((int)b[1] * 255 / b[3]); 320 b[2] = (uint8)((int)b[2] * 255 / b[3]); 321 } 322 b += 4; 323 } 324 bits += bpr; 325 } 326 } 327 328 // #pragma mark - 329 330 typedef agg::conv_transform<VertexSource, Transformation> ScaledPath; 331 typedef agg::conv_transform<ScaledPath, HintingTransformer> HintedPath; 332 333 // _Render 334 void 335 IconRenderer::_Render(const BRect& r) 336 { 337 if (!fIcon) 338 return; 339 340 // TODO: fix clip box for "clear" and "apply_gamma_inv" 341 // fBaseRendererPre.clip_box((int)floorf(r.left), 342 // (int)floorf(r.top), 343 // (int)ceilf(r.right), 344 // (int)ceilf(r.bottom)); 345 346 if (fBackground) 347 memcpy(fBitmap->Bits(), fBackground->Bits(), fBitmap->BitsLength()); 348 else 349 fBaseRendererPre.clear(fBackgroundColor); 350 351 //bigtime_t start = system_time(); 352 StyleHandler styleHandler(fGammaTable); 353 354 fRasterizer.reset(); 355 // iterate over the shapes in the icon, 356 // add the vector paths to the rasterizer 357 // and associate each shapes style 358 int32 shapeCount = fIcon->Shapes()->CountShapes(); 359 int32 styleIndex = 0; 360 for (int32 i = 0; i < shapeCount; i++) { 361 Shape* shape = fIcon->Shapes()->ShapeAtFast(i); 362 363 Transformation transform(*shape); 364 transform.multiply(fGlobalTransform); 365 // NOTE: this works only because "agg::trans_affine", 366 // "Transformable" and "Transformation" are all the 367 // same thing 368 369 // don't render shape if the Level Of Detail falls 370 // out of range 371 if (transform.scale() <= shape->MinVisibilityScale() 372 || transform.scale() > shape->MaxVisibilityScale()) 373 continue; 374 375 Style* style = shape->Style(); 376 if (!style) 377 continue; 378 379 // add the style either with global transformation or with 380 // the shapes transformation, depending on wether there 381 // is a gradient and its settings 382 Gradient* gradient = style->Gradient(); 383 bool styleAdded = false; 384 if (gradient && !gradient->InheritTransformation()) { 385 styleAdded = styleHandler.AddStyle(shape->Style(), 386 fGlobalTransform); 387 } else { 388 styleAdded = styleHandler.AddStyle(shape->Style(), 389 transform); 390 } 391 392 if (!styleAdded) { 393 printf("IconRenderer::_Render() - out of memory\n"); 394 break; 395 } 396 fRasterizer.styles(styleIndex, -1); 397 styleIndex++; 398 399 // global scale 400 shape->SetGlobalScale(max_c(1.0, transform.scale())); 401 ScaledPath scaledPath(shape->VertexSource(), transform); 402 if (shape->Hinting()) { 403 // additional hinting 404 HintingTransformer hinter; 405 HintedPath hintedPath(scaledPath, hinter); 406 fRasterizer.add_path(hintedPath); 407 } else { 408 fRasterizer.add_path(scaledPath); 409 } 410 } 411 412 agg::render_scanlines_compound(fRasterizer, 413 fScanline, 414 fBinaryScanline, 415 fBaseRendererPre, 416 fSpanAllocator, 417 styleHandler); 418 419 if (fGammaTable.gamma() != 1.0) 420 fPixelFormat.apply_gamma_inv(fGammaTable); 421 422 //if (fRenderingBuffer.width() == 64) 423 //printf("rendering 64x64: %lld\n", system_time() - start); 424 } 425 426 427 428 429 430