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