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 // That's unless the scale is bigger than the maximum 352 // MaxVisibilityScale of 4.0f. 353 if (fGlobalTransform.scale() < shape->MinVisibilityScale() 354 || (fGlobalTransform.scale() > shape->MaxVisibilityScale() 355 && shape->MaxVisibilityScale() < 4.0f)) { 356 continue; 357 } 358 359 Transformation transform(*shape); 360 transform.multiply(fGlobalTransform); 361 // NOTE: this works only because "agg::trans_affine", 362 // "Transformable" and "Transformation" are all the 363 // same thing 364 365 Style* style = shape->Style(); 366 if (!style) 367 continue; 368 369 // add the style either with global transformation or with 370 // the shapes transformation, depending on wether there 371 // is a gradient and its settings 372 Gradient* gradient = style->Gradient(); 373 bool styleAdded = false; 374 if (gradient && !gradient->InheritTransformation()) { 375 styleAdded = styleHandler.AddStyle(shape->Style(), 376 fGlobalTransform); 377 } else { 378 styleAdded = styleHandler.AddStyle(shape->Style(), transform); 379 } 380 381 if (!styleAdded) { 382 printf("IconRenderer::_Render() - out of memory\n"); 383 break; 384 } 385 386 // if this is not the first shape, and the style contains 387 // transparency, commit a render pass of previous shapes 388 if (i > 0 && style->HasTransparency()) 389 _CommitRenderPass(styleHandler); 390 391 fRasterizer.styles(styleIndex, -1); 392 styleIndex++; 393 394 // global scale 395 shape->SetGlobalScale(max_c(1.0, transform.scale())); 396 ScaledPath scaledPath(shape->VertexSource(), transform); 397 if (shape->Hinting()) { 398 // additional hinting 399 HintingTransformer hinter; 400 HintedPath hintedPath(scaledPath, hinter); 401 fRasterizer.add_path(hintedPath); 402 } else { 403 fRasterizer.add_path(scaledPath); 404 } 405 } 406 407 _CommitRenderPass(styleHandler, false); 408 409 if (fGammaTable.gamma() != 1.0) 410 fPixelFormat.apply_gamma_inv(fGammaTable); 411 412 //if (fRenderingBuffer.width() == 64) 413 //printf("rendering 64x64: %lld\n", system_time() - start); 414 } 415 416 // _CommitRenderPass 417 void 418 IconRenderer::_CommitRenderPass(StyleHandler& styleHandler, bool reset) 419 { 420 agg::render_scanlines_compound(fRasterizer, fScanline, fBinaryScanline, 421 fBaseRendererPre, fSpanAllocator, styleHandler); 422 423 if (reset) 424 fRasterizer.reset(); 425 } 426 427 428 429 430