1 /* 2 * Copyright 2006, 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 10 #define NANOSVG_IMPLEMENTATION 11 #include "DocumentBuilder.h" 12 13 #include <new> 14 #include <stdio.h> 15 16 #include <Bitmap.h> 17 18 #include "AutoDeleter.h" 19 #include "GradientTransformable.h" 20 #include "Icon.h" 21 #include "PathContainer.h" 22 #include "Shape.h" 23 #include "ShapeContainer.h" 24 #include "StrokeTransformer.h" 25 #include "Style.h" 26 #include "StyleContainer.h" 27 #include "SVGImporter.h" 28 #include "VectorPath.h" 29 30 #include <agg_math_stroke.h> 31 #include <agg_trans_affine.h> 32 33 using std::nothrow; 34 35 // constructor 36 DocumentBuilder::DocumentBuilder(NSVGimage* source) 37 : fWidth(0), 38 fHeight(0), 39 fViewBox(0.0, 0.0, -1.0, -1.0), 40 fTitle(""), 41 fSource(source) 42 { 43 } 44 45 46 // SetTitle 47 void 48 DocumentBuilder::SetTitle(const char* title) 49 { 50 fTitle = title; 51 } 52 53 // SetDimensions 54 void 55 DocumentBuilder::SetDimensions(uint32 width, uint32 height, BRect viewBox) 56 { 57 fWidth = width; 58 fHeight = height; 59 fViewBox = viewBox; 60 } 61 62 // #pragma mark - 63 64 // GetIcon 65 status_t 66 DocumentBuilder::GetIcon(Icon* icon, SVGImporter* importer, 67 const char* fallbackName) 68 { 69 double xMin = 0; 70 double yMin = 0; 71 double xMax = ceil(fSource->width); 72 double yMax = ceil(fSource->height); 73 74 BRect bounds; 75 if (fViewBox.IsValid()) { 76 bounds = fViewBox; 77 printf("view box: "); 78 bounds.PrintToStream(); 79 } else { 80 bounds.Set(0.0, 0.0, (int32)fWidth - 1, (int32)fHeight - 1); 81 printf("width/height: "); 82 bounds.PrintToStream(); 83 } 84 85 BRect boundingBox(xMin, yMin, xMax, yMax); 86 87 if (!bounds.IsValid() || !boundingBox.Intersects(bounds)) { 88 bounds = boundingBox; 89 printf("using bounding box: "); 90 bounds.PrintToStream(); 91 } 92 93 float size = max_c(bounds.Width(), bounds.Height()); 94 double scale = 64.0 / size; 95 printf("scale: %f\n", scale); 96 97 Transformable transform; 98 transform.TranslateBy(BPoint(-bounds.left, -bounds.top)); 99 transform.ScaleBy(B_ORIGIN, scale, scale); 100 101 // if (fTitle.CountChars() > 0) 102 // icon->SetName(fTitle.String()); 103 // else 104 // icon->SetName(fallbackName); 105 106 for (NSVGshape* shape = fSource->shapes; shape != NULL; shape = shape->next) { 107 if (shape->fill.type != NSVG_PAINT_NONE) 108 _AddShape(shape, false, transform, icon); 109 if (shape->stroke.type != NSVG_PAINT_NONE) 110 _AddShape(shape, true, transform, icon); 111 } 112 113 // clean up styles and paths (remove duplicates) 114 int32 count = icon->Shapes()->CountShapes(); 115 for (int32 i = 1; i < count; i++) { 116 Shape* shape = icon->Shapes()->ShapeAtFast(i); 117 Style* style = shape->Style(); 118 if (!style) 119 continue; 120 int32 styleIndex = icon->Styles()->IndexOf(style); 121 for (int32 j = 0; j < styleIndex; j++) { 122 Style* earlierStyle = icon->Styles()->StyleAtFast(j); 123 if (*style == *earlierStyle) { 124 shape->SetStyle(earlierStyle); 125 icon->Styles()->RemoveStyle(style); 126 style->ReleaseReference(); 127 break; 128 } 129 } 130 } 131 132 return B_OK; 133 } 134 135 136 // AddVertexSource 137 status_t 138 AddPathsFromVertexSource(Icon* icon, Shape* shape, NSVGshape* svgShape) 139 { 140 //printf("AddPathsFromVertexSource(pathID = %ld)\n", index); 141 142 for (NSVGpath* svgPath = svgShape->paths; svgPath != NULL; 143 svgPath = svgPath->next) { 144 VectorPath* path = new (nothrow) VectorPath(); 145 if (!path || !icon->Paths()->AddPath(path)) { 146 delete path; 147 return B_NO_MEMORY; 148 } 149 150 if (!shape->Paths()->AddPath(path)) 151 return B_NO_MEMORY; 152 153 path->SetClosed(svgPath->closed); 154 155 int pointCount = svgPath->npts; 156 float* points = svgPath->pts; 157 158 // First entry in the points list is always a "move" to the path 159 // starting point 160 if (!path->AddPoint(BPoint(points[0], points[1]))) 161 return B_NO_MEMORY; 162 path->SetInOutConnected(path->CountPoints() - 1, false); 163 164 pointCount--; 165 points += 2; 166 167 while (pointCount > 0) { 168 BPoint vector1(points[0], points[1]); 169 BPoint vector2(points[2], points[3]); 170 BPoint endPoint(points[4], points[5]); 171 172 if (!path->AddPoint(endPoint)) 173 return B_NO_MEMORY; 174 175 int32 start = path->CountPoints() - 2; 176 int32 end = path->CountPoints() - 1; 177 178 path->SetInOutConnected(end, false); 179 path->SetPointOut(start, vector1); 180 path->SetPointIn(end, vector2); 181 182 pointCount -= 3; 183 points += 6; 184 } 185 } 186 187 // FIXME handle closed vs open paths 188 189 return B_OK; 190 } 191 192 193 // _AddShape 194 status_t 195 DocumentBuilder::_AddShape(NSVGshape* svgShape, bool outline, 196 const Transformable& transform, Icon* icon) 197 { 198 Shape* shape = new (nothrow) Shape(NULL); 199 if (!shape || !icon->Shapes()->AddShape(shape)) { 200 delete shape; 201 return B_NO_MEMORY; 202 } 203 204 if (AddPathsFromVertexSource(icon, shape, svgShape) < B_OK) 205 printf("failed to convert from vertex source\n"); 206 207 shape->SetName(svgShape->id); 208 shape->Multiply(transform); 209 210 StrokeTransformer* stroke = NULL; 211 NSVGpaint* paint = NULL; 212 if (outline) { 213 stroke = new (nothrow) StrokeTransformer(shape->VertexSource()); 214 paint = &svgShape->stroke; 215 216 if (stroke) { 217 stroke->width(svgShape->strokeWidth); 218 switch(svgShape->strokeLineCap) { 219 case NSVG_CAP_BUTT: 220 stroke->line_cap(agg::butt_cap); 221 break; 222 case NSVG_CAP_ROUND: 223 stroke->line_cap(agg::round_cap); 224 break; 225 case NSVG_CAP_SQUARE: 226 stroke->line_cap(agg::square_cap); 227 break; 228 } 229 230 switch(svgShape->strokeLineJoin) { 231 case NSVG_JOIN_MITER: 232 stroke->line_join(agg::miter_join); 233 break; 234 case NSVG_JOIN_ROUND: 235 stroke->line_join(agg::round_join); 236 break; 237 case NSVG_JOIN_BEVEL: 238 stroke->line_join(agg::bevel_join); 239 break; 240 } 241 } 242 243 if (!shape->AddTransformer(stroke)) { 244 delete stroke; 245 stroke = NULL; 246 } 247 } else { 248 paint = &svgShape->fill; 249 #if 0 // FIXME filling rule are missing from Shape class 250 if (svgShape->fillRule == NSVG_FILLRULE_EVENODD) 251 shape->SetFillingRule(FILL_MODE_EVEN_ODD); 252 else 253 shape->SetFillingRule(FILL_MODE_NON_ZERO); 254 #endif 255 } 256 257 Gradient gradient(true); 258 rgb_color color; 259 switch(paint->type) { 260 case NSVG_PAINT_COLOR: 261 color.red = paint->color & 0xFF; 262 color.green = (paint->color >> 8) & 0xFF; 263 color.blue = (paint->color >> 16) & 0xFF; 264 color.alpha = (paint->color >> 24) & 0xFF; 265 break; 266 case NSVG_PAINT_LINEAR_GRADIENT: 267 { 268 gradient.SetType(GRADIENT_LINEAR); 269 // The base gradient axis in Icon-O-Matic is a horizontal line from 270 // (-64, 0) to (64, 0). The base gradient axis used by nanosvg is 271 // a vertical line from (0, 0) to (0, 1). This initial transform 272 // converts from one space to the other. 273 agg::trans_affine baseTransform(0, 1.0/128.0, -1.0/128.0, 0, 274 -0.5, 0.5); 275 gradient.multiply(baseTransform); 276 break; 277 } 278 case NSVG_PAINT_RADIAL_GRADIENT: 279 { 280 gradient.SetType(GRADIENT_CIRCULAR); 281 agg::trans_affine baseTransform(0, 1.0/64.0, -1.0/64.0, 0, 282 0, 0); 283 gradient.multiply(baseTransform); 284 break; 285 } 286 } 287 288 if (paint->type != NSVG_PAINT_COLOR) { 289 gradient.SetInterpolation(INTERPOLATION_LINEAR); 290 agg::trans_affine gradientTransform( 291 paint->gradient->xform[0], paint->gradient->xform[1], 292 paint->gradient->xform[2], paint->gradient->xform[3], 293 paint->gradient->xform[4], paint->gradient->xform[5] 294 ); 295 296 // The transform from nanosvg converts a screen space coordinate into 297 // a gradient offset. It is the inverse of what we need at this stage, 298 // so we just invert it and multiply our base transform with it. 299 gradient.multiply_inv(gradientTransform); 300 301 // Finally, scale the gradient according to the global scaling to fit 302 // the 64x64 box we work in. 303 gradient.Multiply(*shape); 304 305 for (int i = 0; i < paint->gradient->nstops; i++) { 306 rgb_color stopColor; 307 stopColor.red = paint->gradient->stops[i].color & 0xFF; 308 stopColor.green = (paint->gradient->stops[i].color >> 8) & 0xFF; 309 stopColor.blue = (paint->gradient->stops[i].color >> 16) & 0xFF; 310 stopColor.alpha = (paint->gradient->stops[i].color >> 24) & 0xFF; 311 gradient.AddColor(stopColor, paint->gradient->stops[i].offset); 312 } 313 314 BGradient::ColorStop* step = gradient.ColorAt(0); 315 if (step) { 316 color.red = step->color.red; 317 color.green = step->color.green; 318 color.blue = step->color.blue; 319 color.alpha = step->color.alpha; 320 } 321 } 322 323 color.alpha = (uint8)(color.alpha * svgShape->opacity); 324 325 Style* style = new (nothrow) Style(color); 326 if (!style || !icon->Styles()->AddStyle(style)) { 327 delete style; 328 return B_NO_MEMORY; 329 } 330 331 // NOTE: quick hack to freeze all transformations (only works because 332 // paths and styles are not used by multiple shapes!!) 333 int32 pathCount = shape->Paths()->CountPaths(); 334 for (int32 i = 0; i < pathCount; i++) { 335 VectorPath* path = shape->Paths()->PathAtFast(i); 336 path->ApplyTransform(*shape); 337 } 338 339 if (stroke) 340 stroke->width(stroke->width() * shape->scale()); 341 342 if (paint->type != NSVG_PAINT_COLOR) 343 style->SetGradient(&gradient); 344 345 shape->Reset(); 346 347 shape->SetStyle(style); 348 349 return B_OK; 350 } 351 352