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