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:
parent
a866ad3d7a
commit
135a658741
20
README.md
20
README.md
@ -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"
|
||||
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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 |
@ -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 |
@ -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 |
@ -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 |
404
src/nanosvg.h
404
src/nanosvg.h
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user