Added viewBox and unit coversion support

- added exact bounds calculation for bezier curves, paths, shapes
- added unit coversion for svg length values (use px internally)
- added viewBox and preserveAspectRatio handling
- removed some test SVGs
This commit is contained in:
Mikko Mononen 2014-01-29 19:50:41 +02:00
parent a866ad3d7a
commit 135a658741
8 changed files with 397 additions and 197 deletions

View File

@ -9,7 +9,18 @@ NanoSVG is a simple stupid single-header-file SVG parse. The output of the parse
The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.
NanoSVG supports a wide range of SVG features, if somehing is missing, feel free to create a pull request!
NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!
The shapes in the SVG images are transformed by the viewBox and converted to specified units.
That is, you should get the same looking data as your designed in your favorite app.
NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
The units passed to NanoVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
DPI (dots-per-inch) controls how the unit conversion is done.
If you don't know or care about the units stuff, "px" and 96 should get you going.
## Rasterizer
@ -25,7 +36,7 @@ The intended usage for the rasterizer is to for example bake icons of different
``` C
// Load
struct NSVGimage* image;
image = nsvgParseFromFile("test.svg.");
image = nsvgParseFromFile("test.svg", "px", 96);
printf("size: %f x %f\n", image->width, image->height);
// Use...
for (shape = image->shapes; shape != NULL; shape = shape->next) {
@ -44,8 +55,11 @@ nsvgDelete(image);
In order to use NanoSVG in your own project, just copy nanosvg.h to your project.
In one C/C++ define `NANOSVG_IMPLEMENTATION` before including the library to expand the NanoSVG implementation in that file.
NanoSVG depends on `stdio.h` and `math.h`, they should be included where the implementation is expanded before including NanoSVG.
``` C
#include <stdio.h>
#include <math.h>
#define NANOSVG_IMPLEMENTATION // Expands implementation
#include "nanosvg.h"
```
@ -53,6 +67,8 @@ In one C/C++ define `NANOSVG_IMPLEMENTATION` before including the library to exp
By default, NanoSVG parses only the most common colors. In order to get support for full list of [SVG color keywords](http://www.w3.org/TR/SVG11/types.html#ColorKeywords), define `NANOSVG_ALL_COLOR_KEYWORDS` before expanding the implementation.
``` C
#include <stdio.h>
#include <math.h>
#define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of color keywords.
#define NANOSVG_IMPLEMENTATION // Expands implementation
#include "nanosvg.h"

View File

@ -29,9 +29,6 @@ struct NSVGimage* g_image = NULL;
static unsigned char bgColor[4] = {205,202,200,255};
static unsigned char lineColor[4] = {0,160,192,255};
static float minf(float a, float b) { return a < b ? a : b; }
static float maxf(float a, float b) { return a > b ? a : b; }
static float distPtSeg(float x, float y, float px, float py, float qx, float qy)
{
float pqx, pqy, dx, dy, d, t;
@ -80,28 +77,6 @@ static void cubicBez(float x1, float y1, float x2, float y2,
}
}
static void calcBounds(struct NSVGimage* image, float* bounds)
{
struct NSVGshape* shape;
struct NSVGpath* path;
int i;
bounds[0] = FLT_MAX;
bounds[1] = FLT_MAX;
bounds[2] = -FLT_MAX;
bounds[3] = -FLT_MAX;
for (shape = image->shapes; shape != NULL; shape = shape->next) {
for (path = shape->paths; path != NULL; path = path->next) {
for (i = 0; i < path->npts; i++) {
float* p = &path->pts[i*2];
bounds[0] = minf(bounds[0], p[0]);
bounds[1] = minf(bounds[1], p[1]);
bounds[2] = maxf(bounds[2], p[0]);
bounds[3] = maxf(bounds[3], p[1]);
}
}
}
}
void drawPath(float* pts, int npts, char closed, float tol)
{
int i;
@ -166,7 +141,7 @@ void drawControlPts(float* pts, int npts, char closed)
void drawframe(GLFWwindow* window)
{
int width = 0, height = 0;
float bounds[4], view[4], cx, cy, w, h, aspect, px;
float view[4], cx, cy, hw, hh, aspect, px;
struct NSVGshape* shape;
struct NSVGpath* path;
@ -183,24 +158,23 @@ void drawframe(GLFWwindow* window)
glLoadIdentity();
// Fit view to bounds
calcBounds(g_image, bounds);
cx = (bounds[0]+bounds[2])/2;
cy = (bounds[3]+bounds[1])/2;
w = (bounds[2]-bounds[0])/2;
h = (bounds[3]-bounds[1])/2;
cx = g_image->width*0.5f;
cy = g_image->height*0.5f;
hw = g_image->width*0.5f;
hh = g_image->height*0.5f;
if (width/w < height/h) {
if (width/hw < height/hh) {
aspect = (float)height / (float)width;
view[0] = cx - w * 1.2f;
view[2] = cx + w * 1.2f;
view[1] = cy - w * 1.2f * aspect;
view[3] = cy + w * 1.2f * aspect;
view[0] = cx - hw * 1.2f;
view[2] = cx + hw * 1.2f;
view[1] = cy - hw * 1.2f * aspect;
view[3] = cy + hw * 1.2f * aspect;
} else {
aspect = (float)width / (float)height;
view[0] = cx - h * 1.2f * aspect;
view[2] = cx + h * 1.2f * aspect;
view[1] = cy - h * 1.2f;
view[3] = cy + h * 1.2f;
view[0] = cx - hh * 1.2f * aspect;
view[2] = cx + hh * 1.2f * aspect;
view[1] = cy - hh * 1.2f;
view[3] = cy + hh * 1.2f;
}
// Size of one pixel.
px = (view[2] - view[1]) / (float)width;
@ -214,6 +188,15 @@ void drawframe(GLFWwindow* window)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
// Draw bounds
glColor4ub(0,0,0,64);
glBegin(GL_LINE_LOOP);
glVertex2f(0, 0);
glVertex2f(g_image->width, 0);
glVertex2f(g_image->width, g_image->height);
glVertex2f(0, g_image->height);
glEnd();
for (shape = g_image->shapes; shape != NULL; shape = shape->next) {
for (path = shape->paths; path != NULL; path = path->next) {
drawPath(path->pts, path->npts, path->closed, px * 1.5f);
@ -253,7 +236,7 @@ int main()
glEnable(GL_LINE_SMOOTH);
g_image = nsvgParseFromFile("../example/23.svg");
g_image = nsvgParseFromFile("../example/nano.svg", "px", 96.0f);
if (g_image == NULL) {
printf("Could not open SVG image.\n");
glfwTerminate();

View File

@ -26,57 +26,22 @@
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvgrast.h"
static float minf(float a, float b) { return a < b ? a : b; }
static float maxf(float a, float b) { return a > b ? a : b; }
static void calcBounds(struct NSVGimage* image, float* bounds)
{
struct NSVGshape* shape;
struct NSVGpath* path;
int i;
bounds[0] = FLT_MAX;
bounds[1] = FLT_MAX;
bounds[2] = -FLT_MAX;
bounds[3] = -FLT_MAX;
for (shape = image->shapes; shape != NULL; shape = shape->next) {
for (path = shape->paths; path != NULL; path = path->next) {
for (i = 0; i < path->npts; i++) {
float* p = &path->pts[i*2];
bounds[0] = minf(bounds[0], p[0]);
bounds[1] = minf(bounds[1], p[1]);
bounds[2] = maxf(bounds[2], p[0]);
bounds[3] = maxf(bounds[3], p[1]);
}
}
}
}
static void getImageSize(struct NSVGimage *image, int* w, int* h)
{
float bounds[4];
if (image->width < 1 || image->height < 1)
calcBounds(image, bounds);
*w = image->width < 1 ? (bounds[2]+1) : image->width;
*h = image->height < 1 ? (bounds[3]+1) : image->height;
}
int main()
{
struct NSVGimage *image = NULL;
struct NSVGrasterizer *rast = NULL;
unsigned char* img = NULL;
int w, h;
const char* filename = "../example/23.svg";
image = nsvgParseFromFile("../example/23.svg");
printf("parsing %s\n", filename);
image = nsvgParseFromFile(filename, "px", 96.0f);
if (image == NULL) {
printf("Could not open SVG image.\n");
goto error;
}
getImageSize(image, &w, &h);
if (w < 1 || h < 1) {
printf("Size of SVG not specified.\n");
goto error;
}
w = image->width;
h = image->height;
rast = nsvgCreateRasterizer();
if (rast == NULL) {
@ -90,8 +55,10 @@ int main()
goto error;
}
printf("rasterizing image %d x %d\n", w, h);
nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);
printf("writing svg.png\n");
stbi_write_png("svg.png", w, h, 4, img, w*4);
error:

View File

@ -1,24 +0,0 @@
<?xml version="1.0" standalone="no"?>
<svg width="320px" height="320px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M300,200 h-150 a150,150 0 1,0 150,-150 z"
fill="red" stroke="rgb(255,32,1)" stroke-width="5" />
<path d="M275,175 v-150 a150,150 0 0,0 -150,150 z"
fill="yellow" stroke="rgb(100%,23%,2%)" stroke-width="5" />
<path d="M 125,75 a100,50 0 0,0 100,50"
style="fill:none; stroke:red; stroke-width:6"/>
<path d="M 125,75 a100,50 0 0,1 100,50"
style="fill:none; stroke:red; stroke-width:6"/>
<path d="M600,350 l 50,-25
a25,25 -30 0,1 50,-25 l 50,-25
a25,50 -30 0,1 50,-25 l 50,-25
a25,75 -30 0,1 50,-25 l 50,-25
a25,100 -30 0,1 50,-25 l 50,-25"
fill="none" stroke="red " stroke-width="5" />
</svg>

Before

Width:  |  Height:  |  Size: 813 B

View File

@ -1,15 +0,0 @@
<?xml version="1.0" standalone="no"?>
<svg width="325px" height="325px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M80 80
A 45 45, 0, 0, 0, 125 125
L 125 80 Z" fill="green"/>
<path d="M230 80
A 45 45, 0, 1, 0, 275 125
L 275 80 Z" fill="red"/>
<path d="M80 230
A 45 45, 0, 0, 1, 125 275
L 125 230 Z" fill="purple"/>
<path d="M230 230
A 45 45, 0, 1, 1, 275 275
L 275 230 Z" fill="blue"/>
</svg>

Before

Width:  |  Height:  |  Size: 505 B

View File

@ -1,15 +0,0 @@
<?xml version="1.0" standalone="no"?>
<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M10 10 C 20 20, 40 20, 50 10" stroke="black" fill="transparent"/>
<path d="M70 10 C 70 20, 120 20, 120 10" stroke="black" fill="transparent"/>
<path d="M130 10 C 120 20, 180 20, 170 10" stroke="black" fill="transparent"/>
<path d="M10 60 C 20 80, 40 80, 50 60" stroke="black" fill="transparent"/>
<path d="M70 60 C 70 80, 110 80, 110 60" stroke="black" fill="transparent"/>
<path d="M130 60 C 120 80, 180 80, 170 60" stroke="black" fill="transparent"/>
<path d="M10 110 C 20 140, 40 140, 50 110" stroke="black" fill="transparent"/>
<path d="M70 110 C 70 140, 110 140, 110 110" stroke="black" fill="transparent"/>
<path d="M130 110 C 120 140, 180 140, 170 110" stroke="black" fill="transparent"/>
</svg>

Before

Width:  |  Height:  |  Size: 855 B

View File

@ -1,4 +0,0 @@
<?xml version="1.0" standalone="no"?>
<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg"">
<path d="M10 80 Q 95 10 180 80" stroke="black" fill="transparent"/>
</svg>

Before

Width:  |  Height:  |  Size: 199 B

View File

@ -22,6 +22,8 @@
*
* Arc calculation code based on canvg (https://code.google.com/p/canvg/)
*
* Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
*
*/
#ifndef NANOSVG_H
@ -31,10 +33,28 @@
extern "C" {
#endif
// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
//
// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.
//
// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!
//
// The shapes in the SVG images are transformed by the viewBox and converted to specified units.
// That is, you should get the same looking data as your designed in your favorite app.
//
// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
//
// The units passed to NanoVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
// DPI (dots-per-inch) controls how the unit conversion is done.
//
// If you don't know or care about the units stuff, "px" and 96 should get you going.
/* Example Usage:
// Load
struct SNVGImage* image;
image = nsvgParseFromFile("test.svg.");
image = nsvgParseFromFile("test.svg", "px", 96);
printf("size: %f x %f\n", image->width, image->height);
// Use...
for (shape = image->shapes; shape != NULL; shape = shape->next) {
@ -54,6 +74,7 @@ struct NSVGpath
float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ...
int npts; // Total number of bezier points.
char closed; // Flag indicating if shapes should be treated as closed.
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
struct NSVGpath* next; // Pointer to next path, or NULL if last element.
};
@ -64,24 +85,23 @@ struct NSVGshape
float strokeWidth; // Stroke width (scaled)
char hasFill; // Flag indicating if fill exists.
char hasStroke; // Flag indicating id store exists
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
struct NSVGpath* paths; // Linked list of paths in the image.
struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
};
struct NSVGimage
{
float width; // Width of the image, or -1.0f of not set.
float height; // Height of the image, or -1.0f of not set.
char wunits[8]; // Units of the width attribute
char hunits[8]; // Units of the height attribute
float width; // Width of the image.
float height; // Height of the image.
struct NSVGshape* shapes; // Linked list of shapes in the image.
};
// Parses SVG file from a file, returns linked list of paths.
struct NSVGimage* nsvgParseFromFile(const char* filename);
// Parses SVG file from a file, returns SVG image as paths.
struct NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
// Parses SVG file from a null terminated string, returns linked list of paths.
struct NSVGimage* nsvgParse(char* input);
// Parses SVG file from a null terminated string, returns SVG image as paths.
struct NSVGimage* nsvgParse(char* input, const char* units, float dpi);
// Deletes list of paths.
void nsvgDelete(struct NSVGimage* image);
@ -98,8 +118,15 @@ void nsvgDelete(struct NSVGimage* image);
#include <stdlib.h>
#include <math.h>
#define NSVG_PI 3.14159265358979323846264338327f
#define NSVG_KAPPA90 0.5522847493f // Lenght proportional to radius of a cubic bezier handle for 90deg arcs.
#define NSVG_PI (3.14159265358979323846264338327f)
#define NSVG_KAPPA90 (0.5522847493f) // Lenght proportional to radius of a cubic bezier handle for 90deg arcs.
#define NSVG_ALIGN_MIN 0
#define NSVG_ALIGN_MID 1
#define NSVG_ALIGN_MAX 2
#define NSVG_ALIGN_NONE 0
#define NSVG_ALIGN_MEET 1
#define NSVG_ALIGN_SLICE 2
#ifdef _MSC_VER
#pragma warning (disable: 4996) // Switch off security warnings
@ -129,6 +156,7 @@ static int nsvg__isnum(char c)
return strchr("0123456789+-.eE", c) != 0;
}
static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; }
static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; }
@ -258,6 +286,7 @@ struct NSVGAttrib
float fillOpacity;
float strokeOpacity;
float strokeWidth;
float fontSize;
char hasFill;
char hasStroke;
char visible;
@ -272,6 +301,9 @@ struct NSVGParser
int cpts;
struct NSVGpath* plist;
struct NSVGimage* image;
float viewMinx, viewMiny, viewWidth, viewHeight;
int alignX, alignY, alignType;
float dpi;
char pathFlag;
char defsFlag;
};
@ -352,6 +384,71 @@ static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t)
*dy = x*t[1] + y*t[3];
}
#define NSVG_EPSILON (1e-12)
static int nsvg__ptInBounds(float* pt, float* bounds)
{
return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3];
}
static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3)
{
float it = 1.0-t;
return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3;
}
static void nsvg__curveBounds(float* bounds, float* curve)
{
int i, j, count;
double roots[2], a, b, c, b2ac, t, v;
float* v0 = &curve[0];
float* v1 = &curve[2];
float* v2 = &curve[4];
float* v3 = &curve[6];
// Start the bounding box by end points
bounds[0] = nsvg__minf(v0[0], v3[0]);
bounds[1] = nsvg__minf(v0[1], v3[1]);
bounds[2] = nsvg__maxf(v0[0], v3[0]);
bounds[3] = nsvg__maxf(v0[1], v3[1]);
// Bezier curve fits inside the convex hull of it's control points.
// If control points are inside the bounds, we're done.
if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds))
return;
// Add bezier curve inflection points in X and Y.
for (i = 0; i < 2; i++) {
a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i];
b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i];
c = 3.0 * v1[i] - 3.0 * v0[i];
count = 0;
if (fabs(a) < NSVG_EPSILON) {
if (fabs(b) > NSVG_EPSILON) {
t = -c / b;
if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
roots[count++] = t;
}
} else {
b2ac = b*b - 4.0*c*a;
if (b2ac > NSVG_EPSILON) {
t = (-b + sqrt(b2ac)) / (2.0 * a);
if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
roots[count++] = t;
t = (-b - sqrt(b2ac)) / (2.0 * a);
if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
roots[count++] = t;
}
}
for (j = 0; j < count; j++) {
v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]);
bounds[0+i] = nsvg__minf(bounds[0+i], (float)v);
bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v);
}
}
}
static struct NSVGParser* nsvg__createParser()
{
struct NSVGParser* p;
@ -362,8 +459,6 @@ static struct NSVGParser* nsvg__createParser()
p->image = (struct NSVGimage*)malloc(sizeof(struct NSVGimage));
if (p->image == NULL) goto error;
memset(p->image, 0, sizeof(struct NSVGimage));
p->image->width = -1.0f;
p->image->height = -1.0f;
// Init style
nsvg__xformSetIdentity(p->attr[0].xform);
@ -474,6 +569,7 @@ static void nsvg__addShape(struct NSVGParser* p)
struct NSVGAttrib* attr = nsvg__getAttr(p);
float scale = 1.0f;
struct NSVGshape *shape, *cur, *prev;
struct NSVGpath* path;
if (p->plist == NULL)
return;
@ -498,6 +594,18 @@ static void nsvg__addShape(struct NSVGParser* p)
shape->paths = p->plist;
p->plist = NULL;
// Calculate shape bounds
shape->bounds[0] = shape->paths->bounds[0];
shape->bounds[1] = shape->paths->bounds[1];
shape->bounds[2] = shape->paths->bounds[2];
shape->bounds[3] = shape->paths->bounds[3];
for (path = shape->paths->next; path != NULL; path = path->next) {
shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]);
shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]);
shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]);
shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]);
}
// Add to tail
prev = NULL;
cur = p->image->shapes;
@ -520,6 +628,8 @@ static void nsvg__addPath(struct NSVGParser* p, char closed)
{
struct NSVGAttrib* attr = nsvg__getAttr(p);
struct NSVGpath* path = NULL;
float bounds[4];
float* curve;
int i;
if (p->npts == 0)
@ -541,6 +651,23 @@ static void nsvg__addPath(struct NSVGParser* p, char closed)
for (i = 0; i < p->npts; ++i)
nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform);
// Find bounds
for (i = 0; i < path->npts-1; i += 3) {
curve = &path->pts[i*2];
nsvg__curveBounds(bounds, curve);
if (i == 0) {
path->bounds[0] = bounds[0];
path->bounds[1] = bounds[1];
path->bounds[2] = bounds[2];
path->bounds[3] = bounds[3];
} else {
path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]);
path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]);
path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]);
path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]);
}
}
path->next = p->plist;
p->plist = path;
@ -605,6 +732,23 @@ static const char* nsvg__getNextPathItem(const char* s, char* it)
return s;
}
static float nsvg__actualWidth(struct NSVGParser* p)
{
return p->viewWidth;
}
static float nsvg__actualHeight(struct NSVGParser* p)
{
return p->viewHeight;
}
static float nsvg__actualLength(struct NSVGParser* p)
{
float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p);
return sqrtf(w*w + h*h) / sqrtf(2.0f);
}
#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16))
static unsigned int nsvg__parseColorHex(const char* str)
@ -824,10 +968,48 @@ static unsigned int nsvg__parseColor(const char* str)
return nsvg__parseColorName(str);
}
static float nsvg__parseFloat(const char* str)
static float nsvg__convertToPixels(struct NSVGParser* p, float val, const char* units, int dir)
{
while (*str == ' ') ++str;
return (float)atof(str);
struct NSVGAttrib* attr;
// Convert units to pixels.
if (units[0] == '\0') {
return val;
} else if (units[0] == 'p' && units[1] == 'x') {
return val;
} else if (units[0] == 'p' && units[1] == 't') {
return val / 72.0f * p->dpi;
} else if (units[0] == 'p' && units[1] == 'c') {
return val / 6.0f * p->dpi;
} else if (units[0] == 'm' && units[1] == 'm') {
return val / 25.4f * p->dpi;
} else if (units[0] == 'c' && units[1] == 'm') {
return val / 2.54f * p->dpi;
} else if (units[0] == 'i' && units[1] == 'n') {
return val * p->dpi;
} else if (p != NULL) {
attr = nsvg__getAttr(p);
if (units[0] == '%') {
if (dir == 0)
return (val/100.0f) * nsvg__actualWidth(p);
else if (dir == 1)
return (val/100.0f) * nsvg__actualHeight(p);
else if (dir == 2)
return (val/100.0f) * nsvg__actualLength(p);
} else if (units[0] == 'e' && units[1] == 'm') {
return val * attr->fontSize;
} else if (units[0] == 'e' && units[1] == 'x') {
return val * attr->fontSize * 0.52f; // x-height of Helvetica.
}
}
return val;
}
static float nsvg__parseFloat(struct NSVGParser* p, const char* str, int dir)
{
float val = 0;
char units[32]="";
sscanf(str, "%f%s", &val, units);
return nsvg__convertToPixels(p, val, units, dir);
}
static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na)
@ -981,7 +1163,7 @@ static int nsvg__parseAttr(struct NSVGParser* p, const char* name, const char* v
attr->fillColor = nsvg__parseColor(value);
}
} else if (strcmp(name, "fill-opacity") == 0) {
attr->fillOpacity = nsvg__parseFloat(value);
attr->fillOpacity = nsvg__parseFloat(p, value, 2);
} else if (strcmp(name, "stroke") == 0) {
if (strcmp(value, "none") == 0) {
attr->hasStroke = 0;
@ -990,9 +1172,11 @@ static int nsvg__parseAttr(struct NSVGParser* p, const char* name, const char* v
attr->strokeColor = nsvg__parseColor(value);
}
} else if (strcmp(name, "stroke-width") == 0) {
attr->strokeWidth = nsvg__parseFloat(value);
attr->strokeWidth = nsvg__parseFloat(p, value, 2);
} else if (strcmp(name, "stroke-opacity") == 0) {
attr->strokeOpacity = nsvg__parseFloat(value);
attr->strokeOpacity = nsvg__parseFloat(p, value, 2);
} else if (strcmp(name, "font-size") == 0) {
attr->fontSize = nsvg__parseFloat(p, value, 2);
} else if (strcmp(name, "transform") == 0) {
nsvg__parseTransform(p, value);
} else {
@ -1527,12 +1711,12 @@ static void nsvg__parseRect(struct NSVGParser* p, const char** attr)
for (i = 0; attr[i]; i += 2) {
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
if (strcmp(attr[i], "x") == 0) x = nsvg__parseFloat(attr[i+1]);
if (strcmp(attr[i], "y") == 0) y = nsvg__parseFloat(attr[i+1]);
if (strcmp(attr[i], "width") == 0) w = nsvg__parseFloat(attr[i+1]);
if (strcmp(attr[i], "height") == 0) h = nsvg__parseFloat(attr[i+1]);
if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseFloat(attr[i+1]));
if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseFloat(attr[i+1]));
if (strcmp(attr[i], "x") == 0) x = nsvg__parseFloat(p, attr[i+1], 0);
if (strcmp(attr[i], "y") == 0) y = nsvg__parseFloat(p, attr[i+1], 1);
if (strcmp(attr[i], "width") == 0) w = nsvg__parseFloat(p, attr[i+1], 0);
if (strcmp(attr[i], "height") == 0) h = nsvg__parseFloat(p, attr[i+1], 1);
if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseFloat(p, attr[i+1], 0));
if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseFloat(p, attr[i+1], 1));
}
}
@ -1579,9 +1763,9 @@ static void nsvg__parseCircle(struct NSVGParser* p, const char** attr)
for (i = 0; attr[i]; i += 2) {
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseFloat(attr[i+1]);
if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseFloat(attr[i+1]);
if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseFloat(attr[i+1]));
if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseFloat(p, attr[i+1], 0);
if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseFloat(p, attr[i+1], 1);
if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseFloat(p, attr[i+1], 2));
}
}
@ -1610,10 +1794,10 @@ static void nsvg__parseEllipse(struct NSVGParser* p, const char** attr)
for (i = 0; attr[i]; i += 2) {
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseFloat(attr[i+1]);
if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseFloat(attr[i+1]);
if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseFloat(attr[i+1]));
if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseFloat(attr[i+1]));
if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseFloat(p, attr[i+1], 0);
if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseFloat(p, attr[i+1], 1);
if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseFloat(p, attr[i+1], 0));
if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseFloat(p, attr[i+1], 1));
}
}
@ -1643,10 +1827,10 @@ static void nsvg__parseLine(struct NSVGParser* p, const char** attr)
for (i = 0; attr[i]; i += 2) {
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseFloat(attr[i + 1]);
if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseFloat(attr[i + 1]);
if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseFloat(attr[i + 1]);
if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseFloat(attr[i + 1]);
if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseFloat(p, attr[i + 1], 0);
if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseFloat(p, attr[i + 1], 1);
if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseFloat(p, attr[i + 1], 0);
if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseFloat(p, attr[i + 1], 1);
}
}
@ -1702,11 +1886,35 @@ static void nsvg__parseSVG(struct NSVGParser* p, const char** attr)
for (i = 0; attr[i]; i += 2) {
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
if (strcmp(attr[i], "width") == 0) {
p->image->wunits[0] = '\0';
sscanf(attr[i + 1], "%f%s", &p->image->width, p->image->wunits);
p->image->width = nsvg__parseFloat(p, attr[i + 1], 0);
} else if (strcmp(attr[i], "height") == 0) {
p->image->hunits[0] = '\0';
sscanf(attr[i + 1], "%f%s", &p->image->height, p->image->hunits);
p->image->height = nsvg__parseFloat(p, attr[i + 1], 1);
} else if (strcmp(attr[i], "viewBox") == 0) {
sscanf(attr[i + 1], "%f%*[%%, \t]%f%*[%%, \t]%f%*[%%, \t]%f", &p->viewMinx, &p->viewMiny, &p->viewWidth, &p->viewHeight);
} else if (strcmp(attr[i], "preserveAspectRatio") == 0) {
if (strstr(attr[i + 1], "none") != 0) {
// No uniform scaling
p->alignType = NSVG_ALIGN_NONE;
} else {
// Parse X align
if (strstr(attr[i + 1], "xMin") != 0)
p->alignX = NSVG_ALIGN_MIN;
else if (strstr(attr[i + 1], "xMid") != 0)
p->alignX = NSVG_ALIGN_MID;
else if (strstr(attr[i + 1], "xMax") != 0)
p->alignX = NSVG_ALIGN_MAX;
// Parse X align
if (strstr(attr[i + 1], "yMin") != 0)
p->alignY = NSVG_ALIGN_MIN;
else if (strstr(attr[i + 1], "yMid") != 0)
p->alignY = NSVG_ALIGN_MID;
else if (strstr(attr[i + 1], "yMax") != 0)
p->alignY = NSVG_ALIGN_MAX;
// Parse meet/slice
p->alignType = NSVG_ALIGN_MEET;
if (strstr(attr[i + 1], "slice") != 0)
p->alignType = NSVG_ALIGN_SLICE;
}
}
}
}
@ -1778,22 +1986,104 @@ static void nsvg__content(void* ud, const char* s)
// empty
}
static void dump(struct NSVGimage* image)
static void nsvg__imageBounds(struct NSVGParser* p, float* bounds)
{
struct NSVGshape *shape;
if (image == NULL) return;
shape = image->shapes;
while (shape != NULL) {
struct NSVGpath* path;
path = shape->paths;
while (path)
path = path->next;
shape = shape->next;
struct NSVGshape* shape;
shape = p->image->shapes;
bounds[0] = shape->bounds[0];
bounds[1] = shape->bounds[1];
bounds[2] = shape->bounds[2];
bounds[3] = shape->bounds[3];
for (shape = shape->next; shape != NULL; shape = shape->next) {
bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]);
bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]);
bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]);
bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]);
}
}
struct NSVGimage* nsvgParse(char* input)
static float nsvg__viewAlign(float content, float container, int type)
{
if (type == NSVG_ALIGN_MIN)
return 0;
else if (type == NSVG_ALIGN_MAX)
return container - content;
// mid
return (container - content) * 0.5f;
}
static void nsvg__scaleToViewbox(struct NSVGParser* p, const char* units)
{
struct NSVGshape* shape;
struct NSVGpath* path;
float tx, ty, sx, sy, us, bounds[4];
int i;
float* pt;
// Guess image size if not set completely.
nsvg__imageBounds(p, bounds);
if (p->viewWidth == 0) {
if (p->image->width > 0)
p->viewWidth = p->image->width;
else
p->viewWidth = bounds[2];
}
if (p->viewHeight == 0) {
if (p->image->height > 0)
p->viewHeight = p->image->height;
else
p->viewHeight = bounds[3];
}
if (p->image->width == 0)
p->image->width = p->viewWidth;
if (p->image->height == 0)
p->image->height = p->viewHeight;
tx = -p->viewMinx;
ty = -p->viewMiny;
sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0;
sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0;
us = 1.0f / nsvg__convertToPixels(NULL, 1.0f, units, 0);
// Fix aspect ratio
if (p->alignType == NSVG_ALIGN_MEET) {
// fit whole image into viewbox
sx = sy = nsvg__minf(sx, sy);
tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx;
ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy;
} else if (p->alignType == NSVG_ALIGN_SLICE) {
// fill whole viewbox with image
sx = sy = nsvg__maxf(sx, sy);
tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx;
ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy;
}
// Transform
sx *= us;
sy *= us;
for (shape = p->image->shapes; shape != NULL; shape = shape->next) {
shape->bounds[0] *= sx;
shape->bounds[1] *= sy;
shape->bounds[2] *= sx;
shape->bounds[3] *= sy;
for (path = shape->paths; path != NULL; path = path->next) {
path->bounds[0] *= sx;
path->bounds[1] *= sy;
path->bounds[2] *= sx;
path->bounds[3] *= sy;
for (i =0; i < path->npts; i++) {
pt = &path->pts[i*2];
pt[0] = (pt[0] + tx) * sx;
pt[1] = (pt[1] + ty) * sy;
}
}
}
sx *= us;
sy *= us;
}
struct NSVGimage* nsvgParse(char* input, const char* units, float dpi)
{
struct NSVGParser* p;
struct NSVGimage* ret = 0;
@ -1802,20 +2092,22 @@ struct NSVGimage* nsvgParse(char* input)
if (p == NULL) {
return NULL;
}
p->dpi = dpi;
nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p);
// Scale to viewBox
nsvg__scaleToViewbox(p, units);
ret = p->image;
p->image = NULL;
dump(ret);
nsvg__deleteParser(p);
return ret;
}
struct NSVGimage* nsvgParseFromFile(const char* filename)
struct NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
{
FILE* fp = NULL;
int size;
@ -1832,7 +2124,7 @@ struct NSVGimage* nsvgParseFromFile(const char* filename)
fread(data, size, 1, fp);
data[size] = '\0'; // Must be null terminated.
fclose(fp);
image = nsvgParse(data);
image = nsvgParse(data, units, dpi);
free(data);
return image;