]> O.S.I.I.S - jp/vkvg.git/commitdiff
split tests in several exe
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Tue, 19 Mar 2019 18:42:25 +0000 (19:42 +0100)
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Tue, 19 Mar 2019 18:42:25 +0000 (19:42 +0100)
31 files changed:
CMakeLists.txt
include/nanosvg.h [deleted file]
tests/clip.c [new file with mode: 0644]
tests/colinear.c [new file with mode: 0644]
tests/common/nanosvg.h [new file with mode: 0644]
tests/common/test.c [new file with mode: 0644]
tests/common/test.h [new file with mode: 0644]
tests/common/vkengine.c [new file with mode: 0644]
tests/common/vkengine.h [new file with mode: 0644]
tests/curve.c [new file with mode: 0644]
tests/curve2.c [new file with mode: 0644]
tests/data/miroir.jpg [new file with mode: 0755]
tests/data/tiger.svg [new file with mode: 0644]
tests/fill.c [new file with mode: 0644]
tests/fill_and_stroke.c [new file with mode: 0644]
tests/gradient.c [new file with mode: 0644]
tests/gradient_transform.c [new file with mode: 0644]
tests/img_surf.c [new file with mode: 0644]
tests/line_caps.c [new file with mode: 0644]
tests/line_join.c [new file with mode: 0644]
tests/multi.c [new file with mode: 0644]
tests/painting.c [new file with mode: 0644]
tests/random_rects.c [new file with mode: 0644]
tests/rect_fill.c [new file with mode: 0644]
tests/simple_paint.c [new file with mode: 0644]
tests/stroke.c [new file with mode: 0644]
tests/svg.c [new file with mode: 0644]
tests/test1.c
tests/text.c [new file with mode: 0644]
tests/vkengine.c [deleted file]
tests/vkengine.h [deleted file]

index 4e5e45cde61c8ee7351ff11ff0035ad22cb6b77b..fbea596d4b2c51caee8f1d0470d821cd6de90820 100644 (file)
@@ -145,22 +145,62 @@ INSTALL(TARGETS vkvg
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
 
+FUNCTION (buildtest TEST_NAME)
+       ADD_EXECUTABLE(${PROJECT_NAME}_${TEST_NAME} "tests/${TEST_NAME}.c" )
+       TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME}_${TEST_NAME} PRIVATE
+               ${Vulkan_INCLUDE_DIRS}
+               ${CMAKE_CURRENT_SOURCE_DIR}/include
+               ${CMAKE_CURRENT_SOURCE_DIR}/src
+               ${CMAKE_CURRENT_SOURCE_DIR}/tests/common
+               ${CMAKE_CURRENT_SOURCE_DIR}/vkh/include
+               ${CMAKE_CURRENT_SOURCE_DIR}/vkh/src
+       )
+       TARGET_LINK_LIBRARIES(${PROJECT_NAME}_${TEST_NAME}
+               #${Vulkan_LIBRARIES}
+               #${GLFW3_LIBRARIES}
+               #vkh_static
+               #vkvg
+               tests_common
+       )
+ENDFUNCTION (buildtest)
+
 if (GLFW3_FOUND)
-       #build test app
-       ADD_EXECUTABLE(${PROJECT_NAME}_test tests/test1.c tests/vkengine.c)
-       TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME}_test PRIVATE
+       ADD_LIBRARY("tests_common" STATIC tests/common/vkengine.c tests/common/test.c)
+       TARGET_INCLUDE_DIRECTORIES(tests_common PRIVATE
                ${Vulkan_INCLUDE_DIRS}
                ${CMAKE_CURRENT_SOURCE_DIR}/include
                ${CMAKE_CURRENT_SOURCE_DIR}/src
+               ${CMAKE_CURRENT_SOURCE_DIR}/tests/common
                ${CMAKE_CURRENT_SOURCE_DIR}/vkh/include
                ${CMAKE_CURRENT_SOURCE_DIR}/vkh/src
        )
-       TARGET_LINK_LIBRARIES(${PROJECT_NAME}_test
+       TARGET_LINK_LIBRARIES(tests_common
                ${Vulkan_LIBRARIES}
                ${GLFW3_LIBRARIES}
                vkh_static
                vkvg
        )
+       file(GLOB_RECURSE DATAS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/tests" "tests/data/*")
+       FOREACH(DATA_FILE ${DATAS})
+               GET_FILENAME_COMPONENT(copy-dest-dir ${CMAKE_CURRENT_BINARY_DIR}/${DATA_FILE} DIRECTORY)
+               SET(copy-output ${CMAKE_CURRENT_BINARY_DIR}/${DATA_FILE})
+               ADD_CUSTOM_COMMAND(
+                  OUTPUT  ${copy-output}
+                  COMMAND ${CMAKE_COMMAND} -E make_directory ${copy-dest-dir}
+                  COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/tests/${DATA_FILE}" "${copy-output}"
+                  COMMENT "Copying ${DATA_FILE} to ${copy-output}"
+                  #DEPENDS ${DATA_FILE}
+                  VERBATIM
+               )
+       ENDFOREACH()
+       add_custom_target("${PROJECT_NAME}_DataCopy" ALL DEPENDS ${DATAS})
+
+       #build test apps
+       file(GLOB TESTS "tests/*.c")
+       FOREACH(TEST ${TESTS})
+               GET_FILENAME_COMPONENT(testname ${TEST} NAME_WE)
+               buildtest(${testname})
+       ENDFOREACH()
 endif ()
 
 MESSAGE(STATUS "\n\n--------------------------------------------------------------------------")
@@ -178,3 +218,4 @@ ELSE ()
 MESSAGE(STATUS "Surface tiling\t= VK_IMAGE_TILING_LINEAR.")
 ENDIF ()
 MESSAGE(STATUS "---------------------------------------------------------------------------\n\n")
+
diff --git a/include/nanosvg.h b/include/nanosvg.h
deleted file mode 100644 (file)
index 65ba2a7..0000000
+++ /dev/null
@@ -1,2925 +0,0 @@
-/*
- * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
- *
- * This software is provided 'as-is', without any express or implied
- * warranty.  In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- *
- * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example
- * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
- *
- * 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
-#define NANOSVG_H
-
-#ifdef __cplusplus
-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
-       NSVGImage* image;
-       image = nsvgParseFromFile("test.svg", "px", 96);
-       printf("size: %f x %f\n", image->width, image->height);
-       // Use...
-       for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) {
-               for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
-                       for (int i = 0; i < path->npts-1; i += 3) {
-                               float* p = &path->pts[i*2];
-                               drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]);
-                       }
-               }
-       }
-       // Delete
-       nsvgDelete(image);
-*/
-
-enum NSVGpaintType {
-       NSVG_PAINT_NONE = 0,
-       NSVG_PAINT_COLOR = 1,
-       NSVG_PAINT_LINEAR_GRADIENT = 2,
-       NSVG_PAINT_RADIAL_GRADIENT = 3
-};
-
-enum NSVGspreadType {
-       NSVG_SPREAD_PAD = 0,
-       NSVG_SPREAD_REFLECT = 1,
-       NSVG_SPREAD_REPEAT = 2
-};
-
-enum NSVGlineJoin {
-       NSVG_JOIN_MITER = 0,
-       NSVG_JOIN_ROUND = 1,
-       NSVG_JOIN_BEVEL = 2
-};
-
-enum NSVGlineCap {
-       NSVG_CAP_BUTT = 0,
-       NSVG_CAP_ROUND = 1,
-       NSVG_CAP_SQUARE = 2
-};
-
-enum NSVGfillRule {
-       NSVG_FILLRULE_NONZERO = 0,
-       NSVG_FILLRULE_EVENODD = 1
-};
-
-enum NSVGflags {
-       NSVG_FLAGS_VISIBLE = 0x01
-};
-
-typedef struct NSVGgradientStop {
-       unsigned int color;
-       float offset;
-} NSVGgradientStop;
-
-typedef struct NSVGgradient {
-       float xform[6];
-       char spread;
-       float fx, fy;
-       int nstops;
-       NSVGgradientStop stops[1];
-} NSVGgradient;
-
-typedef struct NSVGpaint {
-       char type;
-       union {
-               unsigned int color;
-               NSVGgradient* gradient;
-       };
-} NSVGpaint;
-
-typedef 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.
-} NSVGpath;
-
-typedef struct NSVGshape
-{
-       char id[64];                            // Optional 'id' attr of the shape or its group
-       NSVGpaint fill;                         // Fill paint
-       NSVGpaint stroke;                       // Stroke paint
-       float opacity;                          // Opacity of the shape.
-       float strokeWidth;                      // Stroke width (scaled).
-       float strokeDashOffset;         // Stroke dash offset (scaled).
-       float strokeDashArray[8];                       // Stroke dash array (scaled).
-       char strokeDashCount;                           // Number of dash values in dash array.
-       char strokeLineJoin;            // Stroke join type.
-       char strokeLineCap;                     // Stroke cap type.
-       float miterLimit;                       // Miter limit
-       char fillRule;                          // Fill rule, see NSVGfillRule.
-       unsigned char flags;            // Logical or of NSVG_FLAGS_* flags
-       float bounds[4];                        // Tight bounding box of the shape [minx,miny,maxx,maxy].
-       NSVGpath* paths;                        // Linked list of paths in the image.
-       struct NSVGshape* next;         // Pointer to next shape, or NULL if last element.
-} NSVGshape;
-
-typedef struct NSVGimage
-{
-       float width;                            // Width of the image.
-       float height;                           // Height of the image.
-       NSVGshape* shapes;                      // Linked list of shapes in the image.
-} NSVGimage;
-
-// Parses SVG file from a file, returns SVG image as paths.
-NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
-
-// Parses SVG file from a null terminated string, returns SVG image as paths.
-// Important note: changes the string.
-NSVGimage* nsvgParse(char* input, const char* units, float dpi);
-
-// Deletes list of paths.
-void nsvgDelete(NSVGimage* image);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // NANOSVG_H
-
-#ifdef NANOSVG_IMPLEMENTATION
-
-#include <string.h>
-#include <stdlib.h>
-#include <math.h>
-
-#define NSVG_PI (3.14159265358979323846264338327f)
-#define NSVG_KAPPA90 (0.5522847493f)   // Length 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
-
-#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0)
-#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16))
-
-#ifdef _MSC_VER
-       #pragma warning (disable: 4996) // Switch off security warnings
-       #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings
-       #ifdef __cplusplus
-       #define NSVG_INLINE inline
-       #else
-       #define NSVG_INLINE
-       #endif
-#else
-       #define NSVG_INLINE inline
-#endif
-
-
-static int nsvg__isspace(char c)
-{
-       return strchr(" \t\n\v\f\r", c) != 0;
-}
-
-static int nsvg__isdigit(char c)
-{
-       return c >= '0' && c <= '9';
-}
-
-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; }
-
-
-// Simple XML parser
-
-#define NSVG_XML_TAG 1
-#define NSVG_XML_CONTENT 2
-#define NSVG_XML_MAX_ATTRIBS 256
-
-static void nsvg__parseContent(char* s,
-                                                          void (*contentCb)(void* ud, const char* s),
-                                                          void* ud)
-{
-       // Trim start white spaces
-       while (*s && nsvg__isspace(*s)) s++;
-       if (!*s) return;
-
-       if (contentCb)
-               (*contentCb)(ud, s);
-}
-
-static void nsvg__parseElement(char* s,
-                                                          void (*startelCb)(void* ud, const char* el, const char** attr),
-                                                          void (*endelCb)(void* ud, const char* el),
-                                                          void* ud)
-{
-       const char* attr[NSVG_XML_MAX_ATTRIBS];
-       int nattr = 0;
-       char* name;
-       int start = 0;
-       int end = 0;
-       char quote;
-
-       // Skip white space after the '<'
-       while (*s && nsvg__isspace(*s)) s++;
-
-       // Check if the tag is end tag
-       if (*s == '/') {
-               s++;
-               end = 1;
-       } else {
-               start = 1;
-       }
-
-       // Skip comments, data and preprocessor stuff.
-       if (!*s || *s == '?' || *s == '!')
-               return;
-
-       // Get tag name
-       name = s;
-       while (*s && !nsvg__isspace(*s)) s++;
-       if (*s) { *s++ = '\0'; }
-
-       // Get attribs
-       while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) {
-               char* name = NULL;
-               char* value = NULL;
-
-               // Skip white space before the attrib name
-               while (*s && nsvg__isspace(*s)) s++;
-               if (!*s) break;
-               if (*s == '/') {
-                       end = 1;
-                       break;
-               }
-               name = s;
-               // Find end of the attrib name.
-               while (*s && !nsvg__isspace(*s) && *s != '=') s++;
-               if (*s) { *s++ = '\0'; }
-               // Skip until the beginning of the value.
-               while (*s && *s != '\"' && *s != '\'') s++;
-               if (!*s) break;
-               quote = *s;
-               s++;
-               // Store value and find the end of it.
-               value = s;
-               while (*s && *s != quote) s++;
-               if (*s) { *s++ = '\0'; }
-
-               // Store only well formed attributes
-               if (name && value) {
-                       attr[nattr++] = name;
-                       attr[nattr++] = value;
-               }
-       }
-
-       // List terminator
-       attr[nattr++] = 0;
-       attr[nattr++] = 0;
-
-       // Call callbacks.
-       if (start && startelCb)
-               (*startelCb)(ud, name, attr);
-       if (end && endelCb)
-               (*endelCb)(ud, name);
-}
-
-int nsvg__parseXML(char* input,
-                                  void (*startelCb)(void* ud, const char* el, const char** attr),
-                                  void (*endelCb)(void* ud, const char* el),
-                                  void (*contentCb)(void* ud, const char* s),
-                                  void* ud)
-{
-       char* s = input;
-       char* mark = s;
-       int state = NSVG_XML_CONTENT;
-       while (*s) {
-               if (*s == '<' && state == NSVG_XML_CONTENT) {
-                       // Start of a tag
-                       *s++ = '\0';
-                       nsvg__parseContent(mark, contentCb, ud);
-                       mark = s;
-                       state = NSVG_XML_TAG;
-               } else if (*s == '>' && state == NSVG_XML_TAG) {
-                       // Start of a content or new tag.
-                       *s++ = '\0';
-                       nsvg__parseElement(mark, startelCb, endelCb, ud);
-                       mark = s;
-                       state = NSVG_XML_CONTENT;
-               } else {
-                       s++;
-               }
-       }
-
-       return 1;
-}
-
-
-/* Simple SVG parser. */
-
-#define NSVG_MAX_ATTR 128
-
-enum NSVGgradientUnits {
-       NSVG_USER_SPACE = 0,
-       NSVG_OBJECT_SPACE = 1
-};
-
-#define NSVG_MAX_DASHES 8
-
-enum NSVGunits {
-       NSVG_UNITS_USER,
-       NSVG_UNITS_PX,
-       NSVG_UNITS_PT,
-       NSVG_UNITS_PC,
-       NSVG_UNITS_MM,
-       NSVG_UNITS_CM,
-       NSVG_UNITS_IN,
-       NSVG_UNITS_PERCENT,
-       NSVG_UNITS_EM,
-       NSVG_UNITS_EX
-};
-
-typedef struct NSVGcoordinate {
-       float value;
-       int units;
-} NSVGcoordinate;
-
-typedef struct NSVGlinearData {
-       NSVGcoordinate x1, y1, x2, y2;
-} NSVGlinearData;
-
-typedef struct NSVGradialData {
-       NSVGcoordinate cx, cy, r, fx, fy;
-} NSVGradialData;
-
-typedef struct NSVGgradientData
-{
-       char id[64];
-       char ref[64];
-       char type;
-       union {
-               NSVGlinearData linear;
-               NSVGradialData radial;
-       };
-       char spread;
-       char units;
-       float xform[6];
-       int nstops;
-       NSVGgradientStop* stops;
-       struct NSVGgradientData* next;
-} NSVGgradientData;
-
-typedef struct NSVGattrib
-{
-       char id[64];
-       float xform[6];
-       unsigned int fillColor;
-       unsigned int strokeColor;
-       float opacity;
-       float fillOpacity;
-       float strokeOpacity;
-       char fillGradient[64];
-       char strokeGradient[64];
-       float strokeWidth;
-       float strokeDashOffset;
-       float strokeDashArray[NSVG_MAX_DASHES];
-       int strokeDashCount;
-       char strokeLineJoin;
-       char strokeLineCap;
-       float miterLimit;
-       char fillRule;
-       float fontSize;
-       unsigned int stopColor;
-       float stopOpacity;
-       float stopOffset;
-       char hasFill;
-       char hasStroke;
-       char visible;
-} NSVGattrib;
-
-typedef struct NSVGparser
-{
-       NSVGattrib attr[NSVG_MAX_ATTR];
-       int attrHead;
-       float* pts;
-       int npts;
-       int cpts;
-       NSVGpath* plist;
-       NSVGimage* image;
-       NSVGgradientData* gradients;
-       NSVGshape* shapesTail;
-       float viewMinx, viewMiny, viewWidth, viewHeight;
-       int alignX, alignY, alignType;
-       float dpi;
-       char pathFlag;
-       char defsFlag;
-} NSVGparser;
-
-static void nsvg__xformIdentity(float* t)
-{
-       t[0] = 1.0f; t[1] = 0.0f;
-       t[2] = 0.0f; t[3] = 1.0f;
-       t[4] = 0.0f; t[5] = 0.0f;
-}
-
-static void nsvg__xformSetTranslation(float* t, float tx, float ty)
-{
-       t[0] = 1.0f; t[1] = 0.0f;
-       t[2] = 0.0f; t[3] = 1.0f;
-       t[4] = tx; t[5] = ty;
-}
-
-static void nsvg__xformSetScale(float* t, float sx, float sy)
-{
-       t[0] = sx; t[1] = 0.0f;
-       t[2] = 0.0f; t[3] = sy;
-       t[4] = 0.0f; t[5] = 0.0f;
-}
-
-static void nsvg__xformSetSkewX(float* t, float a)
-{
-       t[0] = 1.0f; t[1] = 0.0f;
-       t[2] = tanf(a); t[3] = 1.0f;
-       t[4] = 0.0f; t[5] = 0.0f;
-}
-
-static void nsvg__xformSetSkewY(float* t, float a)
-{
-       t[0] = 1.0f; t[1] = tanf(a);
-       t[2] = 0.0f; t[3] = 1.0f;
-       t[4] = 0.0f; t[5] = 0.0f;
-}
-
-static void nsvg__xformSetRotation(float* t, float a)
-{
-       float cs = cosf(a), sn = sinf(a);
-       t[0] = cs; t[1] = sn;
-       t[2] = -sn; t[3] = cs;
-       t[4] = 0.0f; t[5] = 0.0f;
-}
-
-static void nsvg__xformMultiply(float* t, float* s)
-{
-       float t0 = t[0] * s[0] + t[1] * s[2];
-       float t2 = t[2] * s[0] + t[3] * s[2];
-       float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
-       t[1] = t[0] * s[1] + t[1] * s[3];
-       t[3] = t[2] * s[1] + t[3] * s[3];
-       t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
-       t[0] = t0;
-       t[2] = t2;
-       t[4] = t4;
-}
-
-static void nsvg__xformInverse(float* inv, float* t)
-{
-       double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1];
-       if (det > -1e-6 && det < 1e-6) {
-               nsvg__xformIdentity(t);
-               return;
-       }
-       invdet = 1.0 / det;
-       inv[0] = (float)(t[3] * invdet);
-       inv[2] = (float)(-t[2] * invdet);
-       inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
-       inv[1] = (float)(-t[1] * invdet);
-       inv[3] = (float)(t[0] * invdet);
-       inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
-}
-
-static void nsvg__xformPremultiply(float* t, float* s)
-{
-       float s2[6];
-       memcpy(s2, s, sizeof(float)*6);
-       nsvg__xformMultiply(s2, t);
-       memcpy(t, s2, sizeof(float)*6);
-}
-
-static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t)
-{
-       *dx = x*t[0] + y*t[2] + t[4];
-       *dy = x*t[1] + y*t[3] + t[5];
-}
-
-static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t)
-{
-       *dx = x*t[0] + y*t[2];
-       *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)
-{
-       double 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 NSVGparser* nsvg__createParser()
-{
-       NSVGparser* p;
-       p = (NSVGparser*)malloc(sizeof(NSVGparser));
-       if (p == NULL) goto error;
-       memset(p, 0, sizeof(NSVGparser));
-
-       p->image = (NSVGimage*)malloc(sizeof(NSVGimage));
-       if (p->image == NULL) goto error;
-       memset(p->image, 0, sizeof(NSVGimage));
-
-       // Init style
-       nsvg__xformIdentity(p->attr[0].xform);
-       memset(p->attr[0].id, 0, sizeof p->attr[0].id);
-       p->attr[0].fillColor = NSVG_RGB(0,0,0);
-       p->attr[0].strokeColor = NSVG_RGB(0,0,0);
-       p->attr[0].opacity = 1;
-       p->attr[0].fillOpacity = 1;
-       p->attr[0].strokeOpacity = 1;
-       p->attr[0].stopOpacity = 1;
-       p->attr[0].strokeWidth = 1;
-       p->attr[0].strokeLineJoin = NSVG_JOIN_MITER;
-       p->attr[0].strokeLineCap = NSVG_CAP_BUTT;
-       p->attr[0].miterLimit = 4;
-       p->attr[0].fillRule = NSVG_FILLRULE_NONZERO;
-       p->attr[0].hasFill = 1;
-       p->attr[0].visible = 1;
-
-       return p;
-
-error:
-       if (p) {
-               if (p->image) free(p->image);
-               free(p);
-       }
-       return NULL;
-}
-
-static void nsvg__deletePaths(NSVGpath* path)
-{
-       while (path) {
-               NSVGpath *next = path->next;
-               if (path->pts != NULL)
-                       free(path->pts);
-               free(path);
-               path = next;
-       }
-}
-
-static void nsvg__deletePaint(NSVGpaint* paint)
-{
-       if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT)
-               free(paint->gradient);
-}
-
-static void nsvg__deleteGradientData(NSVGgradientData* grad)
-{
-       NSVGgradientData* next;
-       while (grad != NULL) {
-               next = grad->next;
-               free(grad->stops);
-               free(grad);
-               grad = next;
-       }
-}
-
-static void nsvg__deleteParser(NSVGparser* p)
-{
-       if (p != NULL) {
-               nsvg__deletePaths(p->plist);
-               nsvg__deleteGradientData(p->gradients);
-               nsvgDelete(p->image);
-               free(p->pts);
-               free(p);
-       }
-}
-
-static void nsvg__resetPath(NSVGparser* p)
-{
-       p->npts = 0;
-}
-
-static void nsvg__addPoint(NSVGparser* p, float x, float y)
-{
-       if (p->npts+1 > p->cpts) {
-               p->cpts = p->cpts ? p->cpts*2 : 8;
-               p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float));
-               if (!p->pts) return;
-       }
-       p->pts[p->npts*2+0] = x;
-       p->pts[p->npts*2+1] = y;
-       p->npts++;
-}
-
-static void nsvg__moveTo(NSVGparser* p, float x, float y)
-{
-       if (p->npts > 0) {
-               p->pts[(p->npts-1)*2+0] = x;
-               p->pts[(p->npts-1)*2+1] = y;
-       } else {
-               nsvg__addPoint(p, x, y);
-       }
-}
-
-static void nsvg__lineTo(NSVGparser* p, float x, float y)
-{
-       float px,py, dx,dy;
-       if (p->npts > 0) {
-               px = p->pts[(p->npts-1)*2+0];
-               py = p->pts[(p->npts-1)*2+1];
-               dx = x - px;
-               dy = y - py;
-               nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f);
-               nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f);
-               nsvg__addPoint(p, x, y);
-       }
-}
-
-static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
-{
-       nsvg__addPoint(p, cpx1, cpy1);
-       nsvg__addPoint(p, cpx2, cpy2);
-       nsvg__addPoint(p, x, y);
-}
-
-static NSVGattrib* nsvg__getAttr(NSVGparser* p)
-{
-       return &p->attr[p->attrHead];
-}
-
-static void nsvg__pushAttr(NSVGparser* p)
-{
-       if (p->attrHead < NSVG_MAX_ATTR-1) {
-               p->attrHead++;
-               memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib));
-       }
-}
-
-static void nsvg__popAttr(NSVGparser* p)
-{
-       if (p->attrHead > 0)
-               p->attrHead--;
-}
-
-static float nsvg__actualOrigX(NSVGparser* p)
-{
-       return p->viewMinx;
-}
-
-static float nsvg__actualOrigY(NSVGparser* p)
-{
-       return p->viewMiny;
-}
-
-static float nsvg__actualWidth(NSVGparser* p)
-{
-       return p->viewWidth;
-}
-
-static float nsvg__actualHeight(NSVGparser* p)
-{
-       return p->viewHeight;
-}
-
-static float nsvg__actualLength(NSVGparser* p)
-{
-       float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p);
-       return sqrtf(w*w + h*h) / sqrtf(2.0f);
-}
-
-static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length)
-{
-       NSVGattrib* attr = nsvg__getAttr(p);
-       switch (c.units) {
-               case NSVG_UNITS_USER:           return c.value;
-               case NSVG_UNITS_PX:                     return c.value;
-               case NSVG_UNITS_PT:                     return c.value / 72.0f * p->dpi;
-               case NSVG_UNITS_PC:                     return c.value / 6.0f * p->dpi;
-               case NSVG_UNITS_MM:                     return c.value / 25.4f * p->dpi;
-               case NSVG_UNITS_CM:                     return c.value / 2.54f * p->dpi;
-               case NSVG_UNITS_IN:                     return c.value * p->dpi;
-               case NSVG_UNITS_EM:                     return c.value * attr->fontSize;
-               case NSVG_UNITS_EX:                     return c.value * attr->fontSize * 0.52f; // x-height of Helvetica.
-               case NSVG_UNITS_PERCENT:        return orig + c.value / 100.0f * length;
-               default:                                        return c.value;
-       }
-       return c.value;
-}
-
-static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
-{
-       NSVGgradientData* grad = p->gradients;
-       while (grad) {
-               if (strcmp(grad->id, id) == 0)
-                       return grad;
-               grad = grad->next;
-       }
-       return NULL;
-}
-
-static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType)
-{
-       NSVGattrib* attr = nsvg__getAttr(p);
-       NSVGgradientData* data = NULL;
-       NSVGgradientData* ref = NULL;
-       NSVGgradientStop* stops = NULL;
-       NSVGgradient* grad;
-       float ox, oy, sw, sh, sl;
-       int nstops = 0;
-
-       data = nsvg__findGradientData(p, id);
-       if (data == NULL) return NULL;
-
-       // TODO: use ref to fill in all unset values too.
-       ref = data;
-       while (ref != NULL) {
-               if (stops == NULL && ref->stops != NULL) {
-                       stops = ref->stops;
-                       nstops = ref->nstops;
-                       break;
-               }
-               ref = nsvg__findGradientData(p, ref->ref);
-       }
-       if (stops == NULL) return NULL;
-
-       grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1));
-       if (grad == NULL) return NULL;
-
-       // The shape width and height.
-       if (data->units == NSVG_OBJECT_SPACE) {
-               ox = localBounds[0];
-               oy = localBounds[1];
-               sw = localBounds[2] - localBounds[0];
-               sh = localBounds[3] - localBounds[1];
-       } else {
-               ox = nsvg__actualOrigX(p);
-               oy = nsvg__actualOrigY(p);
-               sw = nsvg__actualWidth(p);
-               sh = nsvg__actualHeight(p);
-       }
-       sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f);
-
-       if (data->type == NSVG_PAINT_LINEAR_GRADIENT) {
-               float x1, y1, x2, y2, dx, dy;
-               x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw);
-               y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh);
-               x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw);
-               y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh);
-               // Calculate transform aligned to the line
-               dx = x2 - x1;
-               dy = y2 - y1;
-               grad->xform[0] = dy; grad->xform[1] = -dx;
-               grad->xform[2] = dx; grad->xform[3] = dy;
-               grad->xform[4] = x1; grad->xform[5] = y1;
-       } else {
-               float cx, cy, fx, fy, r;
-               cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw);
-               cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh);
-               fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw);
-               fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh);
-               r = nsvg__convertToPixels(p, data->radial.r, 0, sl);
-               // Calculate transform aligned to the circle
-               grad->xform[0] = r; grad->xform[1] = 0;
-               grad->xform[2] = 0; grad->xform[3] = r;
-               grad->xform[4] = cx; grad->xform[5] = cy;
-               grad->fx = fx / r;
-               grad->fy = fy / r;
-       }
-
-       nsvg__xformMultiply(grad->xform, data->xform);
-       nsvg__xformMultiply(grad->xform, attr->xform);
-
-       grad->spread = data->spread;
-       memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop));
-       grad->nstops = nstops;
-
-       *paintType = data->type;
-
-       return grad;
-}
-
-static float nsvg__getAverageScale(float* t)
-{
-       float sx = sqrtf(t[0]*t[0] + t[2]*t[2]);
-       float sy = sqrtf(t[1]*t[1] + t[3]*t[3]);
-       return (sx + sy) * 0.5f;
-}
-
-static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform)
-{
-       NSVGpath* path;
-       float curve[4*2], curveBounds[4];
-       int i, first = 1;
-       for (path = shape->paths; path != NULL; path = path->next) {
-               nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform);
-               for (i = 0; i < path->npts-1; i += 3) {
-                       nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform);
-                       nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform);
-                       nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform);
-                       nsvg__curveBounds(curveBounds, curve);
-                       if (first) {
-                               bounds[0] = curveBounds[0];
-                               bounds[1] = curveBounds[1];
-                               bounds[2] = curveBounds[2];
-                               bounds[3] = curveBounds[3];
-                               first = 0;
-                       } else {
-                               bounds[0] = nsvg__minf(bounds[0], curveBounds[0]);
-                               bounds[1] = nsvg__minf(bounds[1], curveBounds[1]);
-                               bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]);
-                               bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]);
-                       }
-                       curve[0] = curve[6];
-                       curve[1] = curve[7];
-               }
-       }
-}
-
-static void nsvg__addShape(NSVGparser* p)
-{
-       NSVGattrib* attr = nsvg__getAttr(p);
-       float scale = 1.0f;
-       NSVGshape* shape;
-       NSVGpath* path;
-       int i;
-
-       if (p->plist == NULL)
-               return;
-
-       shape = (NSVGshape*)malloc(sizeof(NSVGshape));
-       if (shape == NULL) goto error;
-       memset(shape, 0, sizeof(NSVGshape));
-
-       memcpy(shape->id, attr->id, sizeof shape->id);
-       scale = nsvg__getAverageScale(attr->xform);
-       shape->strokeWidth = attr->strokeWidth * scale;
-       shape->strokeDashOffset = attr->strokeDashOffset * scale;
-       shape->strokeDashCount = (char)attr->strokeDashCount;
-       for (i = 0; i < attr->strokeDashCount; i++)
-               shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
-       shape->strokeLineJoin = attr->strokeLineJoin;
-       shape->strokeLineCap = attr->strokeLineCap;
-       shape->miterLimit = attr->miterLimit;
-       shape->fillRule = attr->fillRule;
-       shape->opacity = attr->opacity;
-
-       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]);
-       }
-
-       // Set fill
-       if (attr->hasFill == 0) {
-               shape->fill.type = NSVG_PAINT_NONE;
-       } else if (attr->hasFill == 1) {
-               shape->fill.type = NSVG_PAINT_COLOR;
-               shape->fill.color = attr->fillColor;
-               shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24;
-       } else if (attr->hasFill == 2) {
-               float inv[6], localBounds[4];
-               nsvg__xformInverse(inv, attr->xform);
-               nsvg__getLocalBounds(localBounds, shape, inv);
-               shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type);
-               if (shape->fill.gradient == NULL) {
-                       shape->fill.type = NSVG_PAINT_NONE;
-               }
-       }
-
-       // Set stroke
-       if (attr->hasStroke == 0) {
-               shape->stroke.type = NSVG_PAINT_NONE;
-       } else if (attr->hasStroke == 1) {
-               shape->stroke.type = NSVG_PAINT_COLOR;
-               shape->stroke.color = attr->strokeColor;
-               shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24;
-       } else if (attr->hasStroke == 2) {
-               float inv[6], localBounds[4];
-               nsvg__xformInverse(inv, attr->xform);
-               nsvg__getLocalBounds(localBounds, shape, inv);
-               shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type);
-               if (shape->stroke.gradient == NULL)
-                       shape->stroke.type = NSVG_PAINT_NONE;
-       }
-
-       // Set flags
-       shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00);
-
-       // Add to tail
-       if (p->image->shapes == NULL)
-               p->image->shapes = shape;
-       else
-               p->shapesTail->next = shape;
-       p->shapesTail = shape;
-
-       return;
-
-error:
-       if (shape) free(shape);
-}
-
-static void nsvg__addPath(NSVGparser* p, char closed)
-{
-       NSVGattrib* attr = nsvg__getAttr(p);
-       NSVGpath* path = NULL;
-       float bounds[4];
-       float* curve;
-       int i;
-
-       if (p->npts < 4)
-               return;
-
-       if (closed)
-               nsvg__lineTo(p, p->pts[0], p->pts[1]);
-
-       path = (NSVGpath*)malloc(sizeof(NSVGpath));
-       if (path == NULL) goto error;
-       memset(path, 0, sizeof(NSVGpath));
-
-       path->pts = (float*)malloc(p->npts*2*sizeof(float));
-       if (path->pts == NULL) goto error;
-       path->closed = closed;
-       path->npts = p->npts;
-
-       // Transform path.
-       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;
-
-       return;
-
-error:
-       if (path != NULL) {
-               if (path->pts != NULL) free(path->pts);
-               free(path);
-       }
-}
-
-// We roll our own string to float because the std library one uses locale and messes things up.
-static double nsvg__atof(const char* s)
-{
-       char* cur = (char*)s;
-       char* end = NULL;
-       double res = 0.0, sign = 1.0;
-       long long intPart = 0, fracPart = 0;
-       char hasIntPart = 0, hasFracPart = 0;
-
-       // Parse optional sign
-       if (*cur == '+') {
-               cur++;
-       } else if (*cur == '-') {
-               sign = -1;
-               cur++;
-       }
-
-       // Parse integer part
-       if (nsvg__isdigit(*cur)) {
-               // Parse digit sequence
-               intPart = (double)strtoll(cur, &end, 10);
-               if (cur != end) {
-                       res = (double)intPart;
-                       hasIntPart = 1;
-                       cur = end;
-               }
-       }
-
-       // Parse fractional part.
-       if (*cur == '.') {
-               cur++; // Skip '.'
-               if (nsvg__isdigit(*cur)) {
-                       // Parse digit sequence
-                       fracPart = strtoll(cur, &end, 10);
-                       if (cur != end) {
-                               res += (double)fracPart / pow(10.0, (double)(end - cur));
-                               hasFracPart = 1;
-                               cur = end;
-                       }
-               }
-       }
-
-       // A valid number should have integer or fractional part.
-       if (!hasIntPart && !hasFracPart)
-               return 0.0;
-
-       // Parse optional exponent
-       if (*cur == 'e' || *cur == 'E') {
-               int expPart = 0;
-               cur++; // skip 'E'
-               expPart = strtol(cur, &end, 10); // Parse digit sequence with sign
-               if (cur != end) {
-                       res *= pow(10.0, (double)expPart);
-               }
-       }
-
-       return res * sign;
-}
-
-
-static const char* nsvg__parseNumber(const char* s, char* it, const int size)
-{
-       const int last = size-1;
-       int i = 0;
-
-       // sign
-       if (*s == '-' || *s == '+') {
-               if (i < last) it[i++] = *s;
-               s++;
-       }
-       // integer part
-       while (*s && nsvg__isdigit(*s)) {
-               if (i < last) it[i++] = *s;
-               s++;
-       }
-       if (*s == '.') {
-               // decimal point
-               if (i < last) it[i++] = *s;
-               s++;
-               // fraction part
-               while (*s && nsvg__isdigit(*s)) {
-                       if (i < last) it[i++] = *s;
-                       s++;
-               }
-       }
-       // exponent
-       if (*s == 'e' || *s == 'E') {
-               if (i < last) it[i++] = *s;
-               s++;
-               if (*s == '-' || *s == '+') {
-                       if (i < last) it[i++] = *s;
-                       s++;
-               }
-               while (*s && nsvg__isdigit(*s)) {
-                       if (i < last) it[i++] = *s;
-                       s++;
-               }
-       }
-       it[i] = '\0';
-
-       return s;
-}
-
-static const char* nsvg__getNextPathItem(const char* s, char* it)
-{
-       it[0] = '\0';
-       // Skip white spaces and commas
-       while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
-       if (!*s) return s;
-       if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
-               s = nsvg__parseNumber(s, it, 64);
-       } else {
-               // Parse command
-               it[0] = *s++;
-               it[1] = '\0';
-               return s;
-       }
-
-       return s;
-}
-
-static unsigned int nsvg__parseColorHex(const char* str)
-{
-       unsigned int c = 0, r = 0, g = 0, b = 0;
-       int n = 0;
-       str++; // skip #
-       // Calculate number of characters.
-       while(str[n] && !nsvg__isspace(str[n]))
-               n++;
-       if (n == 6) {
-               sscanf(str, "%x", &c);
-       } else if (n == 3) {
-               sscanf(str, "%x", &c);
-               c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8);
-               c |= c<<4;
-       }
-       r = (c >> 16) & 0xff;
-       g = (c >> 8) & 0xff;
-       b = c & 0xff;
-       return NSVG_RGB(r,g,b);
-}
-
-static unsigned int nsvg__parseColorRGB(const char* str)
-{
-       int r = -1, g = -1, b = -1;
-       char s1[32]="", s2[32]="";
-       sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b);
-       if (strchr(s1, '%')) {
-               return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100);
-       } else {
-               return NSVG_RGB(r,g,b);
-       }
-}
-
-typedef struct NSVGNamedColor {
-       const char* name;
-       unsigned int color;
-} NSVGNamedColor;
-
-NSVGNamedColor nsvg__colors[] = {
-
-       { "red", NSVG_RGB(255, 0, 0) },
-       { "green", NSVG_RGB( 0, 128, 0) },
-       { "blue", NSVG_RGB( 0, 0, 255) },
-       { "yellow", NSVG_RGB(255, 255, 0) },
-       { "cyan", NSVG_RGB( 0, 255, 255) },
-       { "magenta", NSVG_RGB(255, 0, 255) },
-       { "black", NSVG_RGB( 0, 0, 0) },
-       { "grey", NSVG_RGB(128, 128, 128) },
-       { "gray", NSVG_RGB(128, 128, 128) },
-       { "white", NSVG_RGB(255, 255, 255) },
-
-#ifdef NANOSVG_ALL_COLOR_KEYWORDS
-       { "aliceblue", NSVG_RGB(240, 248, 255) },
-       { "antiquewhite", NSVG_RGB(250, 235, 215) },
-       { "aqua", NSVG_RGB( 0, 255, 255) },
-       { "aquamarine", NSVG_RGB(127, 255, 212) },
-       { "azure", NSVG_RGB(240, 255, 255) },
-       { "beige", NSVG_RGB(245, 245, 220) },
-       { "bisque", NSVG_RGB(255, 228, 196) },
-       { "blanchedalmond", NSVG_RGB(255, 235, 205) },
-       { "blueviolet", NSVG_RGB(138, 43, 226) },
-       { "brown", NSVG_RGB(165, 42, 42) },
-       { "burlywood", NSVG_RGB(222, 184, 135) },
-       { "cadetblue", NSVG_RGB( 95, 158, 160) },
-       { "chartreuse", NSVG_RGB(127, 255, 0) },
-       { "chocolate", NSVG_RGB(210, 105, 30) },
-       { "coral", NSVG_RGB(255, 127, 80) },
-       { "cornflowerblue", NSVG_RGB(100, 149, 237) },
-       { "cornsilk", NSVG_RGB(255, 248, 220) },
-       { "crimson", NSVG_RGB(220, 20, 60) },
-       { "darkblue", NSVG_RGB( 0, 0, 139) },
-       { "darkcyan", NSVG_RGB( 0, 139, 139) },
-       { "darkgoldenrod", NSVG_RGB(184, 134, 11) },
-       { "darkgray", NSVG_RGB(169, 169, 169) },
-       { "darkgreen", NSVG_RGB( 0, 100, 0) },
-       { "darkgrey", NSVG_RGB(169, 169, 169) },
-       { "darkkhaki", NSVG_RGB(189, 183, 107) },
-       { "darkmagenta", NSVG_RGB(139, 0, 139) },
-       { "darkolivegreen", NSVG_RGB( 85, 107, 47) },
-       { "darkorange", NSVG_RGB(255, 140, 0) },
-       { "darkorchid", NSVG_RGB(153, 50, 204) },
-       { "darkred", NSVG_RGB(139, 0, 0) },
-       { "darksalmon", NSVG_RGB(233, 150, 122) },
-       { "darkseagreen", NSVG_RGB(143, 188, 143) },
-       { "darkslateblue", NSVG_RGB( 72, 61, 139) },
-       { "darkslategray", NSVG_RGB( 47, 79, 79) },
-       { "darkslategrey", NSVG_RGB( 47, 79, 79) },
-       { "darkturquoise", NSVG_RGB( 0, 206, 209) },
-       { "darkviolet", NSVG_RGB(148, 0, 211) },
-       { "deeppink", NSVG_RGB(255, 20, 147) },
-       { "deepskyblue", NSVG_RGB( 0, 191, 255) },
-       { "dimgray", NSVG_RGB(105, 105, 105) },
-       { "dimgrey", NSVG_RGB(105, 105, 105) },
-       { "dodgerblue", NSVG_RGB( 30, 144, 255) },
-       { "firebrick", NSVG_RGB(178, 34, 34) },
-       { "floralwhite", NSVG_RGB(255, 250, 240) },
-       { "forestgreen", NSVG_RGB( 34, 139, 34) },
-       { "fuchsia", NSVG_RGB(255, 0, 255) },
-       { "gainsboro", NSVG_RGB(220, 220, 220) },
-       { "ghostwhite", NSVG_RGB(248, 248, 255) },
-       { "gold", NSVG_RGB(255, 215, 0) },
-       { "goldenrod", NSVG_RGB(218, 165, 32) },
-       { "greenyellow", NSVG_RGB(173, 255, 47) },
-       { "honeydew", NSVG_RGB(240, 255, 240) },
-       { "hotpink", NSVG_RGB(255, 105, 180) },
-       { "indianred", NSVG_RGB(205, 92, 92) },
-       { "indigo", NSVG_RGB( 75, 0, 130) },
-       { "ivory", NSVG_RGB(255, 255, 240) },
-       { "khaki", NSVG_RGB(240, 230, 140) },
-       { "lavender", NSVG_RGB(230, 230, 250) },
-       { "lavenderblush", NSVG_RGB(255, 240, 245) },
-       { "lawngreen", NSVG_RGB(124, 252, 0) },
-       { "lemonchiffon", NSVG_RGB(255, 250, 205) },
-       { "lightblue", NSVG_RGB(173, 216, 230) },
-       { "lightcoral", NSVG_RGB(240, 128, 128) },
-       { "lightcyan", NSVG_RGB(224, 255, 255) },
-       { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) },
-       { "lightgray", NSVG_RGB(211, 211, 211) },
-       { "lightgreen", NSVG_RGB(144, 238, 144) },
-       { "lightgrey", NSVG_RGB(211, 211, 211) },
-       { "lightpink", NSVG_RGB(255, 182, 193) },
-       { "lightsalmon", NSVG_RGB(255, 160, 122) },
-       { "lightseagreen", NSVG_RGB( 32, 178, 170) },
-       { "lightskyblue", NSVG_RGB(135, 206, 250) },
-       { "lightslategray", NSVG_RGB(119, 136, 153) },
-       { "lightslategrey", NSVG_RGB(119, 136, 153) },
-       { "lightsteelblue", NSVG_RGB(176, 196, 222) },
-       { "lightyellow", NSVG_RGB(255, 255, 224) },
-       { "lime", NSVG_RGB( 0, 255, 0) },
-       { "limegreen", NSVG_RGB( 50, 205, 50) },
-       { "linen", NSVG_RGB(250, 240, 230) },
-       { "maroon", NSVG_RGB(128, 0, 0) },
-       { "mediumaquamarine", NSVG_RGB(102, 205, 170) },
-       { "mediumblue", NSVG_RGB( 0, 0, 205) },
-       { "mediumorchid", NSVG_RGB(186, 85, 211) },
-       { "mediumpurple", NSVG_RGB(147, 112, 219) },
-       { "mediumseagreen", NSVG_RGB( 60, 179, 113) },
-       { "mediumslateblue", NSVG_RGB(123, 104, 238) },
-       { "mediumspringgreen", NSVG_RGB( 0, 250, 154) },
-       { "mediumturquoise", NSVG_RGB( 72, 209, 204) },
-       { "mediumvioletred", NSVG_RGB(199, 21, 133) },
-       { "midnightblue", NSVG_RGB( 25, 25, 112) },
-       { "mintcream", NSVG_RGB(245, 255, 250) },
-       { "mistyrose", NSVG_RGB(255, 228, 225) },
-       { "moccasin", NSVG_RGB(255, 228, 181) },
-       { "navajowhite", NSVG_RGB(255, 222, 173) },
-       { "navy", NSVG_RGB( 0, 0, 128) },
-       { "oldlace", NSVG_RGB(253, 245, 230) },
-       { "olive", NSVG_RGB(128, 128, 0) },
-       { "olivedrab", NSVG_RGB(107, 142, 35) },
-       { "orange", NSVG_RGB(255, 165, 0) },
-       { "orangered", NSVG_RGB(255, 69, 0) },
-       { "orchid", NSVG_RGB(218, 112, 214) },
-       { "palegoldenrod", NSVG_RGB(238, 232, 170) },
-       { "palegreen", NSVG_RGB(152, 251, 152) },
-       { "paleturquoise", NSVG_RGB(175, 238, 238) },
-       { "palevioletred", NSVG_RGB(219, 112, 147) },
-       { "papayawhip", NSVG_RGB(255, 239, 213) },
-       { "peachpuff", NSVG_RGB(255, 218, 185) },
-       { "peru", NSVG_RGB(205, 133, 63) },
-       { "pink", NSVG_RGB(255, 192, 203) },
-       { "plum", NSVG_RGB(221, 160, 221) },
-       { "powderblue", NSVG_RGB(176, 224, 230) },
-       { "purple", NSVG_RGB(128, 0, 128) },
-       { "rosybrown", NSVG_RGB(188, 143, 143) },
-       { "royalblue", NSVG_RGB( 65, 105, 225) },
-       { "saddlebrown", NSVG_RGB(139, 69, 19) },
-       { "salmon", NSVG_RGB(250, 128, 114) },
-       { "sandybrown", NSVG_RGB(244, 164, 96) },
-       { "seagreen", NSVG_RGB( 46, 139, 87) },
-       { "seashell", NSVG_RGB(255, 245, 238) },
-       { "sienna", NSVG_RGB(160, 82, 45) },
-       { "silver", NSVG_RGB(192, 192, 192) },
-       { "skyblue", NSVG_RGB(135, 206, 235) },
-       { "slateblue", NSVG_RGB(106, 90, 205) },
-       { "slategray", NSVG_RGB(112, 128, 144) },
-       { "slategrey", NSVG_RGB(112, 128, 144) },
-       { "snow", NSVG_RGB(255, 250, 250) },
-       { "springgreen", NSVG_RGB( 0, 255, 127) },
-       { "steelblue", NSVG_RGB( 70, 130, 180) },
-       { "tan", NSVG_RGB(210, 180, 140) },
-       { "teal", NSVG_RGB( 0, 128, 128) },
-       { "thistle", NSVG_RGB(216, 191, 216) },
-       { "tomato", NSVG_RGB(255, 99, 71) },
-       { "turquoise", NSVG_RGB( 64, 224, 208) },
-       { "violet", NSVG_RGB(238, 130, 238) },
-       { "wheat", NSVG_RGB(245, 222, 179) },
-       { "whitesmoke", NSVG_RGB(245, 245, 245) },
-       { "yellowgreen", NSVG_RGB(154, 205, 50) },
-#endif
-};
-
-static unsigned int nsvg__parseColorName(const char* str)
-{
-       int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor);
-
-       for (i = 0; i < ncolors; i++) {
-               if (strcmp(nsvg__colors[i].name, str) == 0) {
-                       return nsvg__colors[i].color;
-               }
-       }
-
-       return NSVG_RGB(128, 128, 128);
-}
-
-static unsigned int nsvg__parseColor(const char* str)
-{
-       size_t len = 0;
-       while(*str == ' ') ++str;
-       len = strlen(str);
-       if (len >= 1 && *str == '#')
-               return nsvg__parseColorHex(str);
-       else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(')
-               return nsvg__parseColorRGB(str);
-       return nsvg__parseColorName(str);
-}
-
-static float nsvg__parseOpacity(const char* str)
-{
-       float val = 0;
-       sscanf(str, "%f", &val);
-       if (val < 0.0f) val = 0.0f;
-       if (val > 1.0f) val = 1.0f;
-       return val;
-}
-
-static float nsvg__parseMiterLimit(const char* str)
-{
-       float val = 0;
-       sscanf(str, "%f", &val);
-       if (val < 0.0f) val = 0.0f;
-       return val;
-}
-
-static int nsvg__parseUnits(const char* units)
-{
-       if (units[0] == 'p' && units[1] == 'x')
-               return NSVG_UNITS_PX;
-       else if (units[0] == 'p' && units[1] == 't')
-               return NSVG_UNITS_PT;
-       else if (units[0] == 'p' && units[1] == 'c')
-               return NSVG_UNITS_PC;
-       else if (units[0] == 'm' && units[1] == 'm')
-               return NSVG_UNITS_MM;
-       else if (units[0] == 'c' && units[1] == 'm')
-               return NSVG_UNITS_CM;
-       else if (units[0] == 'i' && units[1] == 'n')
-               return NSVG_UNITS_IN;
-       else if (units[0] == '%')
-               return NSVG_UNITS_PERCENT;
-       else if (units[0] == 'e' && units[1] == 'm')
-               return NSVG_UNITS_EM;
-       else if (units[0] == 'e' && units[1] == 'x')
-               return NSVG_UNITS_EX;
-       return NSVG_UNITS_USER;
-}
-
-static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str)
-{
-       NSVGcoordinate coord = {0, NSVG_UNITS_USER};
-       char units[32]="";
-       sscanf(str, "%f%31s", &coord.value, units);
-       coord.units = nsvg__parseUnits(units);
-       return coord;
-}
-
-static NSVGcoordinate nsvg__coord(float v, int units)
-{
-       NSVGcoordinate coord = {v, units};
-       return coord;
-}
-
-static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length)
-{
-       NSVGcoordinate coord = nsvg__parseCoordinateRaw(str);
-       return nsvg__convertToPixels(p, coord, orig, length);
-}
-
-static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na)
-{
-       const char* end;
-       const char* ptr;
-       char it[64];
-
-       *na = 0;
-       ptr = str;
-       while (*ptr && *ptr != '(') ++ptr;
-       if (*ptr == 0)
-               return 1;
-       end = ptr;
-       while (*end && *end != ')') ++end;
-       if (*end == 0)
-               return 1;
-
-       while (ptr < end) {
-               if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) {
-                       if (*na >= maxNa) return 0;
-                       ptr = nsvg__parseNumber(ptr, it, 64);
-                       args[(*na)++] = (float)nsvg__atof(it);
-               } else {
-                       ++ptr;
-               }
-       }
-       return (int)(end - str);
-}
-
-
-static int nsvg__parseMatrix(float* xform, const char* str)
-{
-       float t[6];
-       int na = 0;
-       int len = nsvg__parseTransformArgs(str, t, 6, &na);
-       if (na != 6) return len;
-       memcpy(xform, t, sizeof(float)*6);
-       return len;
-}
-
-static int nsvg__parseTranslate(float* xform, const char* str)
-{
-       float args[2];
-       float t[6];
-       int na = 0;
-       int len = nsvg__parseTransformArgs(str, args, 2, &na);
-       if (na == 1) args[1] = 0.0;
-
-       nsvg__xformSetTranslation(t, args[0], args[1]);
-       memcpy(xform, t, sizeof(float)*6);
-       return len;
-}
-
-static int nsvg__parseScale(float* xform, const char* str)
-{
-       float args[2];
-       int na = 0;
-       float t[6];
-       int len = nsvg__parseTransformArgs(str, args, 2, &na);
-       if (na == 1) args[1] = args[0];
-       nsvg__xformSetScale(t, args[0], args[1]);
-       memcpy(xform, t, sizeof(float)*6);
-       return len;
-}
-
-static int nsvg__parseSkewX(float* xform, const char* str)
-{
-       float args[1];
-       int na = 0;
-       float t[6];
-       int len = nsvg__parseTransformArgs(str, args, 1, &na);
-       nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI);
-       memcpy(xform, t, sizeof(float)*6);
-       return len;
-}
-
-static int nsvg__parseSkewY(float* xform, const char* str)
-{
-       float args[1];
-       int na = 0;
-       float t[6];
-       int len = nsvg__parseTransformArgs(str, args, 1, &na);
-       nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI);
-       memcpy(xform, t, sizeof(float)*6);
-       return len;
-}
-
-static int nsvg__parseRotate(float* xform, const char* str)
-{
-       float args[3];
-       int na = 0;
-       float m[6];
-       float t[6];
-       int len = nsvg__parseTransformArgs(str, args, 3, &na);
-       if (na == 1)
-               args[1] = args[2] = 0.0f;
-       nsvg__xformIdentity(m);
-
-       if (na > 1) {
-               nsvg__xformSetTranslation(t, -args[1], -args[2]);
-               nsvg__xformMultiply(m, t);
-       }
-
-       nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI);
-       nsvg__xformMultiply(m, t);
-
-       if (na > 1) {
-               nsvg__xformSetTranslation(t, args[1], args[2]);
-               nsvg__xformMultiply(m, t);
-       }
-
-       memcpy(xform, m, sizeof(float)*6);
-
-       return len;
-}
-
-static void nsvg__parseTransform(float* xform, const char* str)
-{
-       float t[6];
-       nsvg__xformIdentity(xform);
-       while (*str)
-       {
-               if (strncmp(str, "matrix", 6) == 0)
-                       str += nsvg__parseMatrix(t, str);
-               else if (strncmp(str, "translate", 9) == 0)
-                       str += nsvg__parseTranslate(t, str);
-               else if (strncmp(str, "scale", 5) == 0)
-                       str += nsvg__parseScale(t, str);
-               else if (strncmp(str, "rotate", 6) == 0)
-                       str += nsvg__parseRotate(t, str);
-               else if (strncmp(str, "skewX", 5) == 0)
-                       str += nsvg__parseSkewX(t, str);
-               else if (strncmp(str, "skewY", 5) == 0)
-                       str += nsvg__parseSkewY(t, str);
-               else{
-                       ++str;
-                       continue;
-               }
-
-               nsvg__xformPremultiply(xform, t);
-       }
-}
-
-static void nsvg__parseUrl(char* id, const char* str)
-{
-       int i = 0;
-       str += 4; // "url(";
-       if (*str == '#')
-               str++;
-       while (i < 63 && *str != ')') {
-               id[i] = *str++;
-               i++;
-       }
-       id[i] = '\0';
-}
-
-static char nsvg__parseLineCap(const char* str)
-{
-       if (strcmp(str, "butt") == 0)
-               return NSVG_CAP_BUTT;
-       else if (strcmp(str, "round") == 0)
-               return NSVG_CAP_ROUND;
-       else if (strcmp(str, "square") == 0)
-               return NSVG_CAP_SQUARE;
-       // TODO: handle inherit.
-       return NSVG_CAP_BUTT;
-}
-
-static char nsvg__parseLineJoin(const char* str)
-{
-       if (strcmp(str, "miter") == 0)
-               return NSVG_JOIN_MITER;
-       else if (strcmp(str, "round") == 0)
-               return NSVG_JOIN_ROUND;
-       else if (strcmp(str, "bevel") == 0)
-               return NSVG_JOIN_BEVEL;
-       // TODO: handle inherit.
-       return NSVG_JOIN_MITER;
-}
-
-static char nsvg__parseFillRule(const char* str)
-{
-       if (strcmp(str, "nonzero") == 0)
-               return NSVG_FILLRULE_NONZERO;
-       else if (strcmp(str, "evenodd") == 0)
-               return NSVG_FILLRULE_EVENODD;
-       // TODO: handle inherit.
-       return NSVG_FILLRULE_NONZERO;
-}
-
-static const char* nsvg__getNextDashItem(const char* s, char* it)
-{
-       int n = 0;
-       it[0] = '\0';
-       // Skip white spaces and commas
-       while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
-       // Advance until whitespace, comma or end.
-       while (*s && (!nsvg__isspace(*s) && *s != ',')) {
-               if (n < 63)
-                       it[n++] = *s;
-               s++;
-       }
-       it[n++] = '\0';
-       return s;
-}
-
-static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray)
-{
-       char item[64];
-       int count = 0, i;
-       float sum = 0.0f;
-
-       // Handle "none"
-       if (str[0] == 'n')
-               return 0;
-
-       // Parse dashes
-       while (*str) {
-               str = nsvg__getNextDashItem(str, item);
-               if (!*item) break;
-               if (count < NSVG_MAX_DASHES)
-                       strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p)));
-       }
-
-       for (i = 0; i < count; i++)
-               sum += strokeDashArray[i];
-       if (sum <= 1e-6f)
-               count = 0;
-
-       return count;
-}
-
-static void nsvg__parseStyle(NSVGparser* p, const char* str);
-
-static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
-{
-       float xform[6];
-       NSVGattrib* attr = nsvg__getAttr(p);
-       if (!attr) return 0;
-
-       if (strcmp(name, "style") == 0) {
-               nsvg__parseStyle(p, value);
-       } else if (strcmp(name, "display") == 0) {
-               if (strcmp(value, "none") == 0)
-                       attr->visible = 0;
-               // Don't reset ->visible on display:inline, one display:none hides the whole subtree
-
-       } else if (strcmp(name, "fill") == 0) {
-               if (strcmp(value, "none") == 0) {
-                       attr->hasFill = 0;
-               } else if (strncmp(value, "url(", 4) == 0) {
-                       attr->hasFill = 2;
-                       nsvg__parseUrl(attr->fillGradient, value);
-               } else {
-                       attr->hasFill = 1;
-                       attr->fillColor = nsvg__parseColor(value);
-               }
-       } else if (strcmp(name, "opacity") == 0) {
-               attr->opacity = nsvg__parseOpacity(value);
-       } else if (strcmp(name, "fill-opacity") == 0) {
-               attr->fillOpacity = nsvg__parseOpacity(value);
-       } else if (strcmp(name, "stroke") == 0) {
-               if (strcmp(value, "none") == 0) {
-                       attr->hasStroke = 0;
-               } else if (strncmp(value, "url(", 4) == 0) {
-                       attr->hasStroke = 2;
-                       nsvg__parseUrl(attr->strokeGradient, value);
-               } else {
-                       attr->hasStroke = 1;
-                       attr->strokeColor = nsvg__parseColor(value);
-               }
-       } else if (strcmp(name, "stroke-width") == 0) {
-               attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
-       } else if (strcmp(name, "stroke-dasharray") == 0) {
-               attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray);
-       } else if (strcmp(name, "stroke-dashoffset") == 0) {
-               attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
-       } else if (strcmp(name, "stroke-opacity") == 0) {
-               attr->strokeOpacity = nsvg__parseOpacity(value);
-       } else if (strcmp(name, "stroke-linecap") == 0) {
-               attr->strokeLineCap = nsvg__parseLineCap(value);
-       } else if (strcmp(name, "stroke-linejoin") == 0) {
-               attr->strokeLineJoin = nsvg__parseLineJoin(value);
-       } else if (strcmp(name, "stroke-miterlimit") == 0) {
-               attr->miterLimit = nsvg__parseMiterLimit(value);
-       } else if (strcmp(name, "fill-rule") == 0) {
-               attr->fillRule = nsvg__parseFillRule(value);
-       } else if (strcmp(name, "font-size") == 0) {
-               attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
-       } else if (strcmp(name, "transform") == 0) {
-               nsvg__parseTransform(xform, value);
-               nsvg__xformPremultiply(attr->xform, xform);
-       } else if (strcmp(name, "stop-color") == 0) {
-               attr->stopColor = nsvg__parseColor(value);
-       } else if (strcmp(name, "stop-opacity") == 0) {
-               attr->stopOpacity = nsvg__parseOpacity(value);
-       } else if (strcmp(name, "offset") == 0) {
-               attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f);
-       } else if (strcmp(name, "id") == 0) {
-               strncpy(attr->id, value, 63);
-               attr->id[63] = '\0';
-       } else {
-               return 0;
-       }
-       return 1;
-}
-
-static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end)
-{
-       const char* str;
-       const char* val;
-       char name[512];
-       char value[512];
-       int n;
-
-       str = start;
-       while (str < end && *str != ':') ++str;
-
-       val = str;
-
-       // Right Trim
-       while (str > start &&  (*str == ':' || nsvg__isspace(*str))) --str;
-       ++str;
-
-       n = (int)(str - start);
-       if (n > 511) n = 511;
-       if (n) memcpy(name, start, n);
-       name[n] = 0;
-
-       while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val;
-
-       n = (int)(end - val);
-       if (n > 511) n = 511;
-       if (n) memcpy(value, val, n);
-       value[n] = 0;
-
-       return nsvg__parseAttr(p, name, value);
-}
-
-static void nsvg__parseStyle(NSVGparser* p, const char* str)
-{
-       const char* start;
-       const char* end;
-
-       while (*str) {
-               // Left Trim
-               while(*str && nsvg__isspace(*str)) ++str;
-               start = str;
-               while(*str && *str != ';') ++str;
-               end = str;
-
-               // Right Trim
-               while (end > start &&  (*end == ';' || nsvg__isspace(*end))) --end;
-               ++end;
-
-               nsvg__parseNameValue(p, start, end);
-               if (*str) ++str;
-       }
-}
-
-static void nsvg__parseAttribs(NSVGparser* p, const char** attr)
-{
-       int i;
-       for (i = 0; attr[i]; i += 2)
-       {
-               if (strcmp(attr[i], "style") == 0)
-                       nsvg__parseStyle(p, attr[i + 1]);
-               else
-                       nsvg__parseAttr(p, attr[i], attr[i + 1]);
-       }
-}
-
-static int nsvg__getArgsPerElement(char cmd)
-{
-       switch (cmd) {
-               case 'v':
-               case 'V':
-               case 'h':
-               case 'H':
-                       return 1;
-               case 'm':
-               case 'M':
-               case 'l':
-               case 'L':
-               case 't':
-               case 'T':
-                       return 2;
-               case 'q':
-               case 'Q':
-               case 's':
-               case 'S':
-                       return 4;
-               case 'c':
-               case 'C':
-                       return 6;
-               case 'a':
-               case 'A':
-                       return 7;
-       }
-       return 0;
-}
-
-static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
-{
-       if (rel) {
-               *cpx += args[0];
-               *cpy += args[1];
-       } else {
-               *cpx = args[0];
-               *cpy = args[1];
-       }
-       nsvg__moveTo(p, *cpx, *cpy);
-}
-
-static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
-{
-       if (rel) {
-               *cpx += args[0];
-               *cpy += args[1];
-       } else {
-               *cpx = args[0];
-               *cpy = args[1];
-       }
-       nsvg__lineTo(p, *cpx, *cpy);
-}
-
-static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
-{
-       if (rel)
-               *cpx += args[0];
-       else
-               *cpx = args[0];
-       nsvg__lineTo(p, *cpx, *cpy);
-}
-
-static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
-{
-       if (rel)
-               *cpy += args[0];
-       else
-               *cpy = args[0];
-       nsvg__lineTo(p, *cpx, *cpy);
-}
-
-static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy,
-                                                                float* cpx2, float* cpy2, float* args, int rel)
-{
-       float x2, y2, cx1, cy1, cx2, cy2;
-
-       if (rel) {
-               cx1 = *cpx + args[0];
-               cy1 = *cpy + args[1];
-               cx2 = *cpx + args[2];
-               cy2 = *cpy + args[3];
-               x2 = *cpx + args[4];
-               y2 = *cpy + args[5];
-       } else {
-               cx1 = args[0];
-               cy1 = args[1];
-               cx2 = args[2];
-               cy2 = args[3];
-               x2 = args[4];
-               y2 = args[5];
-       }
-
-       nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
-
-       *cpx2 = cx2;
-       *cpy2 = cy2;
-       *cpx = x2;
-       *cpy = y2;
-}
-
-static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy,
-                                                                         float* cpx2, float* cpy2, float* args, int rel)
-{
-       float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
-
-       x1 = *cpx;
-       y1 = *cpy;
-       if (rel) {
-               cx2 = *cpx + args[0];
-               cy2 = *cpy + args[1];
-               x2 = *cpx + args[2];
-               y2 = *cpy + args[3];
-       } else {
-               cx2 = args[0];
-               cy2 = args[1];
-               x2 = args[2];
-               y2 = args[3];
-       }
-
-       cx1 = 2*x1 - *cpx2;
-       cy1 = 2*y1 - *cpy2;
-
-       nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
-
-       *cpx2 = cx2;
-       *cpy2 = cy2;
-       *cpx = x2;
-       *cpy = y2;
-}
-
-static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy,
-                                                               float* cpx2, float* cpy2, float* args, int rel)
-{
-       float x1, y1, x2, y2, cx, cy;
-       float cx1, cy1, cx2, cy2;
-
-       x1 = *cpx;
-       y1 = *cpy;
-       if (rel) {
-               cx = *cpx + args[0];
-               cy = *cpy + args[1];
-               x2 = *cpx + args[2];
-               y2 = *cpy + args[3];
-       } else {
-               cx = args[0];
-               cy = args[1];
-               x2 = args[2];
-               y2 = args[3];
-       }
-
-       // Convert to cubic bezier
-       cx1 = x1 + 2.0f/3.0f*(cx - x1);
-       cy1 = y1 + 2.0f/3.0f*(cy - y1);
-       cx2 = x2 + 2.0f/3.0f*(cx - x2);
-       cy2 = y2 + 2.0f/3.0f*(cy - y2);
-
-       nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
-
-       *cpx2 = cx;
-       *cpy2 = cy;
-       *cpx = x2;
-       *cpy = y2;
-}
-
-static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy,
-                                                                        float* cpx2, float* cpy2, float* args, int rel)
-{
-       float x1, y1, x2, y2, cx, cy;
-       float cx1, cy1, cx2, cy2;
-
-       x1 = *cpx;
-       y1 = *cpy;
-       if (rel) {
-               x2 = *cpx + args[0];
-               y2 = *cpy + args[1];
-       } else {
-               x2 = args[0];
-               y2 = args[1];
-       }
-
-       cx = 2*x1 - *cpx2;
-       cy = 2*y1 - *cpy2;
-
-       // Convert to cubix bezier
-       cx1 = x1 + 2.0f/3.0f*(cx - x1);
-       cy1 = y1 + 2.0f/3.0f*(cy - y1);
-       cx2 = x2 + 2.0f/3.0f*(cx - x2);
-       cy2 = y2 + 2.0f/3.0f*(cy - y2);
-
-       nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
-
-       *cpx2 = cx;
-       *cpy2 = cy;
-       *cpx = x2;
-       *cpy = y2;
-}
-
-static float nsvg__sqr(float x) { return x*x; }
-static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); }
-
-static float nsvg__vecrat(float ux, float uy, float vx, float vy)
-{
-       return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy));
-}
-
-static float nsvg__vecang(float ux, float uy, float vx, float vy)
-{
-       float r = nsvg__vecrat(ux,uy, vx,vy);
-       if (r < -1.0f) r = -1.0f;
-       if (r > 1.0f) r = 1.0f;
-       return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r);
-}
-
-static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
-{
-       // Ported from canvg (https://code.google.com/p/canvg/)
-       float rx, ry, rotx;
-       float x1, y1, x2, y2, cx, cy, dx, dy, d;
-       float x1p, y1p, cxp, cyp, s, sa, sb;
-       float ux, uy, vx, vy, a1, da;
-       float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6];
-       float sinrx, cosrx;
-       int fa, fs;
-       int i, ndivs;
-       float hda, kappa;
-
-       rx = fabsf(args[0]);                            // y radius
-       ry = fabsf(args[1]);                            // x radius
-       rotx = args[2] / 180.0f * NSVG_PI;              // x rotation angle
-       fa = fabsf(args[3]) > 1e-6 ? 1 : 0;     // Large arc
-       fs = fabsf(args[4]) > 1e-6 ? 1 : 0;     // Sweep direction
-       x1 = *cpx;                                                      // start point
-       y1 = *cpy;
-       if (rel) {                                                      // end point
-               x2 = *cpx + args[5];
-               y2 = *cpy + args[6];
-       } else {
-               x2 = args[5];
-               y2 = args[6];
-       }
-
-       dx = x1 - x2;
-       dy = y1 - y2;
-       d = sqrtf(dx*dx + dy*dy);
-       if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) {
-               // The arc degenerates to a line
-               nsvg__lineTo(p, x2, y2);
-               *cpx = x2;
-               *cpy = y2;
-               return;
-       }
-
-       sinrx = sinf(rotx);
-       cosrx = cosf(rotx);
-
-       // Convert to center point parameterization.
-       // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
-       // 1) Compute x1', y1'
-       x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f;
-       y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f;
-       d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry);
-       if (d > 1) {
-               d = sqrtf(d);
-               rx *= d;
-               ry *= d;
-       }
-       // 2) Compute cx', cy'
-       s = 0.0f;
-       sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p);
-       sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p);
-       if (sa < 0.0f) sa = 0.0f;
-       if (sb > 0.0f)
-               s = sqrtf(sa / sb);
-       if (fa == fs)
-               s = -s;
-       cxp = s * rx * y1p / ry;
-       cyp = s * -ry * x1p / rx;
-
-       // 3) Compute cx,cy from cx',cy'
-       cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp;
-       cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp;
-
-       // 4) Calculate theta1, and delta theta.
-       ux = (x1p - cxp) / rx;
-       uy = (y1p - cyp) / ry;
-       vx = (-x1p - cxp) / rx;
-       vy = (-y1p - cyp) / ry;
-       a1 = nsvg__vecang(1.0f,0.0f, ux,uy);    // Initial angle
-       da = nsvg__vecang(ux,uy, vx,vy);                // Delta angle
-
-//     if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI;
-//     if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0;
-
-       if (fs == 0 && da > 0)
-               da -= 2 * NSVG_PI;
-       else if (fs == 1 && da < 0)
-               da += 2 * NSVG_PI;
-
-       // Approximate the arc using cubic spline segments.
-       t[0] = cosrx; t[1] = sinrx;
-       t[2] = -sinrx; t[3] = cosrx;
-       t[4] = cx; t[5] = cy;
-
-       // Split arc into max 90 degree segments.
-       // The loop assumes an iteration per end point (including start and end), this +1.
-       ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f);
-       hda = (da / (float)ndivs) / 2.0f;
-       kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
-       if (da < 0.0f)
-               kappa = -kappa;
-
-       for (i = 0; i <= ndivs; i++) {
-               a = a1 + da * ((float)i/(float)ndivs);
-               dx = cosf(a);
-               dy = sinf(a);
-               nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position
-               nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent
-               if (i > 0)
-                       nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y);
-               px = x;
-               py = y;
-               ptanx = tanx;
-               ptany = tany;
-       }
-
-       *cpx = x2;
-       *cpy = y2;
-}
-
-static void nsvg__parsePath(NSVGparser* p, const char** attr)
-{
-       const char* s = NULL;
-       char cmd = '\0';
-       float args[10];
-       int nargs;
-       int rargs = 0;
-       float cpx, cpy, cpx2, cpy2;
-       const char* tmp[4];
-       char closedFlag;
-       int i;
-       char item[64];
-
-       for (i = 0; attr[i]; i += 2) {
-               if (strcmp(attr[i], "d") == 0) {
-                       s = attr[i + 1];
-               } else {
-                       tmp[0] = attr[i];
-                       tmp[1] = attr[i + 1];
-                       tmp[2] = 0;
-                       tmp[3] = 0;
-                       nsvg__parseAttribs(p, tmp);
-               }
-       }
-
-       if (s) {
-               nsvg__resetPath(p);
-               cpx = 0; cpy = 0;
-               cpx2 = 0; cpy2 = 0;
-               closedFlag = 0;
-               nargs = 0;
-
-               while (*s) {
-                       s = nsvg__getNextPathItem(s, item);
-                       if (!*item) break;
-                       if (nsvg__isnum(item[0])) {
-                               if (nargs < 10)
-                                       args[nargs++] = (float)nsvg__atof(item);
-                               if (nargs >= rargs) {
-                                       switch (cmd) {
-                                               case 'm':
-                                               case 'M':
-                                                       nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0);
-                                                       // Moveto can be followed by multiple coordinate pairs,
-                                                       // which should be treated as linetos.
-                                                       cmd = (cmd == 'm') ? 'l' : 'L';
-                                                       rargs = nsvg__getArgsPerElement(cmd);
-                                                       cpx2 = cpx; cpy2 = cpy;
-                                                       break;
-                                               case 'l':
-                                               case 'L':
-                                                       nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
-                                                       cpx2 = cpx; cpy2 = cpy;
-                                                       break;
-                                               case 'H':
-                                               case 'h':
-                                                       nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
-                                                       cpx2 = cpx; cpy2 = cpy;
-                                                       break;
-                                               case 'V':
-                                               case 'v':
-                                                       nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
-                                                       cpx2 = cpx; cpy2 = cpy;
-                                                       break;
-                                               case 'C':
-                                               case 'c':
-                                                       nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
-                                                       break;
-                                               case 'S':
-                                               case 's':
-                                                       nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
-                                                       break;
-                                               case 'Q':
-                                               case 'q':
-                                                       nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
-                                                       break;
-                                               case 'T':
-                                               case 't':
-                                                       nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0);
-                                                       break;
-                                               case 'A':
-                                               case 'a':
-                                                       nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
-                                                       cpx2 = cpx; cpy2 = cpy;
-                                                       break;
-                                               default:
-                                                       if (nargs >= 2) {
-                                                               cpx = args[nargs-2];
-                                                               cpy = args[nargs-1];
-                                                               cpx2 = cpx; cpy2 = cpy;
-                                                       }
-                                                       break;
-                                       }
-                                       nargs = 0;
-                               }
-                       } else {
-                               cmd = item[0];
-                               rargs = nsvg__getArgsPerElement(cmd);
-                               if (cmd == 'M' || cmd == 'm') {
-                                       // Commit path.
-                                       if (p->npts > 0)
-                                               nsvg__addPath(p, closedFlag);
-                                       // Start new subpath.
-                                       nsvg__resetPath(p);
-                                       closedFlag = 0;
-                                       nargs = 0;
-                               } else if (cmd == 'Z' || cmd == 'z') {
-                                       closedFlag = 1;
-                                       // Commit path.
-                                       if (p->npts > 0) {
-                                               // Move current point to first point
-                                               cpx = p->pts[0];
-                                               cpy = p->pts[1];
-                                               cpx2 = cpx; cpy2 = cpy;
-                                               nsvg__addPath(p, closedFlag);
-                                       }
-                                       // Start new subpath.
-                                       nsvg__resetPath(p);
-                                       nsvg__moveTo(p, cpx, cpy);
-                                       closedFlag = 0;
-                                       nargs = 0;
-                               }
-                       }
-               }
-               // Commit path.
-               if (p->npts)
-                       nsvg__addPath(p, closedFlag);
-       }
-
-       nsvg__addShape(p);
-}
-
-static void nsvg__parseRect(NSVGparser* p, const char** attr)
-{
-       float x = 0.0f;
-       float y = 0.0f;
-       float w = 0.0f;
-       float h = 0.0f;
-       float rx = -1.0f; // marks not set
-       float ry = -1.0f;
-       int i;
-
-       for (i = 0; attr[i]; i += 2) {
-               if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
-                       if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
-                       if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
-                       if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p));
-                       if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p));
-                       if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
-                       if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
-               }
-       }
-
-       if (rx < 0.0f && ry > 0.0f) rx = ry;
-       if (ry < 0.0f && rx > 0.0f) ry = rx;
-       if (rx < 0.0f) rx = 0.0f;
-       if (ry < 0.0f) ry = 0.0f;
-       if (rx > w/2.0f) rx = w/2.0f;
-       if (ry > h/2.0f) ry = h/2.0f;
-
-       if (w != 0.0f && h != 0.0f) {
-               nsvg__resetPath(p);
-
-               if (rx < 0.00001f || ry < 0.0001f) {
-                       nsvg__moveTo(p, x, y);
-                       nsvg__lineTo(p, x+w, y);
-                       nsvg__lineTo(p, x+w, y+h);
-                       nsvg__lineTo(p, x, y+h);
-               } else {
-                       // Rounded rectangle
-                       nsvg__moveTo(p, x+rx, y);
-                       nsvg__lineTo(p, x+w-rx, y);
-                       nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry);
-                       nsvg__lineTo(p, x+w, y+h-ry);
-                       nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h);
-                       nsvg__lineTo(p, x+rx, y+h);
-                       nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry);
-                       nsvg__lineTo(p, x, y+ry);
-                       nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y);
-               }
-
-               nsvg__addPath(p, 1);
-
-               nsvg__addShape(p);
-       }
-}
-
-static void nsvg__parseCircle(NSVGparser* p, const char** attr)
-{
-       float cx = 0.0f;
-       float cy = 0.0f;
-       float r = 0.0f;
-       int i;
-
-       for (i = 0; attr[i]; i += 2) {
-               if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
-                       if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
-                       if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
-                       if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p)));
-               }
-       }
-
-       if (r > 0.0f) {
-               nsvg__resetPath(p);
-
-               nsvg__moveTo(p, cx+r, cy);
-               nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r);
-               nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy);
-               nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r);
-               nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy);
-
-               nsvg__addPath(p, 1);
-
-               nsvg__addShape(p);
-       }
-}
-
-static void nsvg__parseEllipse(NSVGparser* p, const char** attr)
-{
-       float cx = 0.0f;
-       float cy = 0.0f;
-       float rx = 0.0f;
-       float ry = 0.0f;
-       int i;
-
-       for (i = 0; attr[i]; i += 2) {
-               if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
-                       if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
-                       if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
-                       if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
-                       if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
-               }
-       }
-
-       if (rx > 0.0f && ry > 0.0f) {
-
-               nsvg__resetPath(p);
-
-               nsvg__moveTo(p, cx+rx, cy);
-               nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry);
-               nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy);
-               nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry);
-               nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy);
-
-               nsvg__addPath(p, 1);
-
-               nsvg__addShape(p);
-       }
-}
-
-static void nsvg__parseLine(NSVGparser* p, const char** attr)
-{
-       float x1 = 0.0;
-       float y1 = 0.0;
-       float x2 = 0.0;
-       float y2 = 0.0;
-       int i;
-
-       for (i = 0; attr[i]; i += 2) {
-               if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
-                       if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
-                       if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
-                       if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
-                       if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
-               }
-       }
-
-       nsvg__resetPath(p);
-
-       nsvg__moveTo(p, x1, y1);
-       nsvg__lineTo(p, x2, y2);
-
-       nsvg__addPath(p, 0);
-
-       nsvg__addShape(p);
-}
-
-static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag)
-{
-       int i;
-       const char* s;
-       float args[2];
-       int nargs, npts = 0;
-       char item[64];
-
-       nsvg__resetPath(p);
-
-       for (i = 0; attr[i]; i += 2) {
-               if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
-                       if (strcmp(attr[i], "points") == 0) {
-                               s = attr[i + 1];
-                               nargs = 0;
-                               while (*s) {
-                                       s = nsvg__getNextPathItem(s, item);
-                                       args[nargs++] = (float)nsvg__atof(item);
-                                       if (nargs >= 2) {
-                                               if (npts == 0)
-                                                       nsvg__moveTo(p, args[0], args[1]);
-                                               else
-                                                       nsvg__lineTo(p, args[0], args[1]);
-                                               nargs = 0;
-                                               npts++;
-                                       }
-                               }
-                       }
-               }
-       }
-
-       nsvg__addPath(p, (char)closeFlag);
-
-       nsvg__addShape(p);
-}
-
-static void nsvg__parseSVG(NSVGparser* p, const char** attr)
-{
-       int i;
-       for (i = 0; attr[i]; i += 2) {
-               if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
-                       if (strcmp(attr[i], "width") == 0) {
-                               p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
-                       } else if (strcmp(attr[i], "height") == 0) {
-                               p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
-                       } 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;
-                               }
-                       }
-               }
-       }
-}
-
-static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type)
-{
-       int i;
-       NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData));
-       if (grad == NULL) return;
-       memset(grad, 0, sizeof(NSVGgradientData));
-       grad->units = NSVG_OBJECT_SPACE;
-       grad->type = type;
-       if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) {
-               grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
-               grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
-               grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT);
-               grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
-       } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) {
-               grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
-               grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
-               grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
-       }
-
-       nsvg__xformIdentity(grad->xform);
-
-       for (i = 0; attr[i]; i += 2) {
-               if (strcmp(attr[i], "id") == 0) {
-                       strncpy(grad->id, attr[i+1], 63);
-                       grad->id[63] = '\0';
-               } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
-                       if (strcmp(attr[i], "gradientUnits") == 0) {
-                               if (strcmp(attr[i+1], "objectBoundingBox") == 0)
-                                       grad->units = NSVG_OBJECT_SPACE;
-                               else
-                                       grad->units = NSVG_USER_SPACE;
-                       } else if (strcmp(attr[i], "gradientTransform") == 0) {
-                               nsvg__parseTransform(grad->xform, attr[i + 1]);
-                       } else if (strcmp(attr[i], "cx") == 0) {
-                               grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]);
-                       } else if (strcmp(attr[i], "cy") == 0) {
-                               grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]);
-                       } else if (strcmp(attr[i], "r") == 0) {
-                               grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]);
-                       } else if (strcmp(attr[i], "fx") == 0) {
-                               grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]);
-                       } else if (strcmp(attr[i], "fy") == 0) {
-                               grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]);
-                       } else if (strcmp(attr[i], "x1") == 0) {
-                               grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]);
-                       } else if (strcmp(attr[i], "y1") == 0) {
-                               grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]);
-                       } else if (strcmp(attr[i], "x2") == 0) {
-                               grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]);
-                       } else if (strcmp(attr[i], "y2") == 0) {
-                               grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]);
-                       } else if (strcmp(attr[i], "spreadMethod") == 0) {
-                               if (strcmp(attr[i+1], "pad") == 0)
-                                       grad->spread = NSVG_SPREAD_PAD;
-                               else if (strcmp(attr[i+1], "reflect") == 0)
-                                       grad->spread = NSVG_SPREAD_REFLECT;
-                               else if (strcmp(attr[i+1], "repeat") == 0)
-                                       grad->spread = NSVG_SPREAD_REPEAT;
-                       } else if (strcmp(attr[i], "xlink:href") == 0) {
-                               const char *href = attr[i+1];
-                               strncpy(grad->ref, href+1, 62);
-                               grad->ref[62] = '\0';
-                       }
-               }
-       }
-
-       grad->next = p->gradients;
-       p->gradients = grad;
-}
-
-static void nsvg__parseGradientStop(NSVGparser* p, const char** attr)
-{
-       NSVGattrib* curAttr = nsvg__getAttr(p);
-       NSVGgradientData* grad;
-       NSVGgradientStop* stop;
-       int i, idx;
-
-       curAttr->stopOffset = 0;
-       curAttr->stopColor = 0;
-       curAttr->stopOpacity = 1.0f;
-
-       for (i = 0; attr[i]; i += 2) {
-               nsvg__parseAttr(p, attr[i], attr[i + 1]);
-       }
-
-       // Add stop to the last gradient.
-       grad = p->gradients;
-       if (grad == NULL) return;
-
-       grad->nstops++;
-       grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops);
-       if (grad->stops == NULL) return;
-
-       // Insert
-       idx = grad->nstops-1;
-       for (i = 0; i < grad->nstops-1; i++) {
-               if (curAttr->stopOffset < grad->stops[i].offset) {
-                       idx = i;
-                       break;
-               }
-       }
-       if (idx != grad->nstops-1) {
-               for (i = grad->nstops-1; i > idx; i--)
-                       grad->stops[i] = grad->stops[i-1];
-       }
-
-       stop = &grad->stops[idx];
-       stop->color = curAttr->stopColor;
-       stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24;
-       stop->offset = curAttr->stopOffset;
-}
-
-static void nsvg__startElement(void* ud, const char* el, const char** attr)
-{
-       NSVGparser* p = (NSVGparser*)ud;
-
-       if (p->defsFlag) {
-               // Skip everything but gradients in defs
-               if (strcmp(el, "linearGradient") == 0) {
-                       nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
-               } else if (strcmp(el, "radialGradient") == 0) {
-                       nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
-               } else if (strcmp(el, "stop") == 0) {
-                       nsvg__parseGradientStop(p, attr);
-               }
-               return;
-       }
-
-       if (strcmp(el, "g") == 0) {
-               nsvg__pushAttr(p);
-               nsvg__parseAttribs(p, attr);
-       } else if (strcmp(el, "path") == 0) {
-               if (p->pathFlag)        // Do not allow nested paths.
-                       return;
-               nsvg__pushAttr(p);
-               nsvg__parsePath(p, attr);
-               nsvg__popAttr(p);
-       } else if (strcmp(el, "rect") == 0) {
-               nsvg__pushAttr(p);
-               nsvg__parseRect(p, attr);
-               nsvg__popAttr(p);
-       } else if (strcmp(el, "circle") == 0) {
-               nsvg__pushAttr(p);
-               nsvg__parseCircle(p, attr);
-               nsvg__popAttr(p);
-       } else if (strcmp(el, "ellipse") == 0) {
-               nsvg__pushAttr(p);
-               nsvg__parseEllipse(p, attr);
-               nsvg__popAttr(p);
-       } else if (strcmp(el, "line") == 0)  {
-               nsvg__pushAttr(p);
-               nsvg__parseLine(p, attr);
-               nsvg__popAttr(p);
-       } else if (strcmp(el, "polyline") == 0)  {
-               nsvg__pushAttr(p);
-               nsvg__parsePoly(p, attr, 0);
-               nsvg__popAttr(p);
-       } else if (strcmp(el, "polygon") == 0)  {
-               nsvg__pushAttr(p);
-               nsvg__parsePoly(p, attr, 1);
-               nsvg__popAttr(p);
-       } else  if (strcmp(el, "linearGradient") == 0) {
-               nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
-       } else if (strcmp(el, "radialGradient") == 0) {
-               nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
-       } else if (strcmp(el, "stop") == 0) {
-               nsvg__parseGradientStop(p, attr);
-       } else if (strcmp(el, "defs") == 0) {
-               p->defsFlag = 1;
-       } else if (strcmp(el, "svg") == 0) {
-               nsvg__parseSVG(p, attr);
-       }
-}
-
-static void nsvg__endElement(void* ud, const char* el)
-{
-       NSVGparser* p = (NSVGparser*)ud;
-
-       if (strcmp(el, "g") == 0) {
-               nsvg__popAttr(p);
-       } else if (strcmp(el, "path") == 0) {
-               p->pathFlag = 0;
-       } else if (strcmp(el, "defs") == 0) {
-               p->defsFlag = 0;
-       }
-}
-
-static void nsvg__content(void* ud, const char* s)
-{
-       NSVG_NOTUSED(ud);
-       NSVG_NOTUSED(s);
-       // empty
-}
-
-static void nsvg__imageBounds(NSVGparser* p, float* bounds)
-{
-       NSVGshape* shape;
-       shape = p->image->shapes;
-       if (shape == NULL) {
-               bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0;
-               return;
-       }
-       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]);
-       }
-}
-
-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__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy)
-{
-       float t[6];
-       nsvg__xformSetTranslation(t, tx, ty);
-       nsvg__xformMultiply (grad->xform, t);
-
-       nsvg__xformSetScale(t, sx, sy);
-       nsvg__xformMultiply (grad->xform, t);
-}
-
-static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
-{
-       NSVGshape* shape;
-       NSVGpath* path;
-       float tx, ty, sx, sy, us, bounds[4], t[6], avgs;
-       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->viewMinx = bounds[0];
-                       p->viewWidth = bounds[2] - bounds[0];
-               }
-       }
-       if (p->viewHeight == 0) {
-               if (p->image->height > 0) {
-                       p->viewHeight = p->image->height;
-               } else {
-                       p->viewMiny = bounds[1];
-                       p->viewHeight = bounds[3] - bounds[1];
-               }
-       }
-       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;
-       // Unit scaling
-       us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f);
-
-       // 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;
-       avgs = (sx+sy) / 2.0f;
-       for (shape = p->image->shapes; shape != NULL; shape = shape->next) {
-               shape->bounds[0] = (shape->bounds[0] + tx) * sx;
-               shape->bounds[1] = (shape->bounds[1] + ty) * sy;
-               shape->bounds[2] = (shape->bounds[2] + tx) * sx;
-               shape->bounds[3] = (shape->bounds[3] + ty) * sy;
-               for (path = shape->paths; path != NULL; path = path->next) {
-                       path->bounds[0] = (path->bounds[0] + tx) * sx;
-                       path->bounds[1] = (path->bounds[1] + ty) * sy;
-                       path->bounds[2] = (path->bounds[2] + tx) * sx;
-                       path->bounds[3] = (path->bounds[3] + ty) * 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;
-                       }
-               }
-
-               if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) {
-                       nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy);
-                       memcpy(t, shape->fill.gradient->xform, sizeof(float)*6);
-                       nsvg__xformInverse(shape->fill.gradient->xform, t);
-               }
-               if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) {
-                       nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy);
-                       memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6);
-                       nsvg__xformInverse(shape->stroke.gradient->xform, t);
-               }
-
-               shape->strokeWidth *= avgs;
-               shape->strokeDashOffset *= avgs;
-               for (i = 0; i < shape->strokeDashCount; i++)
-                       shape->strokeDashArray[i] *= avgs;
-       }
-}
-
-NSVGimage* nsvgParse(char* input, const char* units, float dpi)
-{
-       NSVGparser* p;
-       NSVGimage* ret = 0;
-
-       p = nsvg__createParser();
-       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;
-
-       nsvg__deleteParser(p);
-
-       return ret;
-}
-
-NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
-{
-       FILE* fp = NULL;
-       size_t size;
-       char* data = NULL;
-       NSVGimage* image = NULL;
-
-       fp = fopen(filename, "rb");
-       if (!fp) goto error;
-       fseek(fp, 0, SEEK_END);
-       size = ftell(fp);
-       fseek(fp, 0, SEEK_SET);
-       data = (char*)malloc(size+1);
-       if (data == NULL) goto error;
-       if (fread(data, 1, size, fp) != size) goto error;
-       data[size] = '\0';      // Must be null terminated.
-       fclose(fp);
-       image = nsvgParse(data, units, dpi);
-       free(data);
-
-       return image;
-
-error:
-       if (fp) fclose(fp);
-       if (data) free(data);
-       if (image) nsvgDelete(image);
-       return NULL;
-}
-
-void nsvgDelete(NSVGimage* image)
-{
-       NSVGshape *snext, *shape;
-       if (image == NULL) return;
-       shape = image->shapes;
-       while (shape != NULL) {
-               snext = shape->next;
-               nsvg__deletePaths(shape->paths);
-               nsvg__deletePaint(&shape->fill);
-               nsvg__deletePaint(&shape->stroke);
-               free(shape);
-               shape = snext;
-       }
-       free(image);
-}
-
-#endif
diff --git a/tests/clip.c b/tests/clip.c
new file mode 100644 (file)
index 0000000..8a39502
--- /dev/null
@@ -0,0 +1,27 @@
+#include "test.h"
+
+void test_clip(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    vkvg_move_to(ctx,10,10);
+    vkvg_line_to(ctx,400,150);
+    vkvg_line_to(ctx,900,10);
+    vkvg_line_to(ctx,700,450);
+    vkvg_line_to(ctx,900,750);
+    vkvg_line_to(ctx,500,650);
+    vkvg_line_to(ctx,100,800);
+    vkvg_line_to(ctx,150,400);
+    vkvg_clip(ctx);
+
+    vkvg_set_source_rgb(ctx,1,0,0);
+    vkvg_paint(ctx);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test_clip, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/colinear.c b/tests/colinear.c
new file mode 100644 (file)
index 0000000..7f426ad
--- /dev/null
@@ -0,0 +1,25 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    vkvg_set_source_rgba(ctx,0.7,0.7,0.7,1);
+    vkvg_paint(ctx);
+
+    vkvg_set_source_rgba(ctx,0,1,0,1);
+    vkvg_set_line_width(ctx,10);
+
+    vkvg_move_to(ctx,100,100);
+    vkvg_line_to(ctx,100,200);
+    vkvg_line_to(ctx,100,100);
+    vkvg_stroke(ctx);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/common/nanosvg.h b/tests/common/nanosvg.h
new file mode 100644 (file)
index 0000000..65ba2a7
--- /dev/null
@@ -0,0 +1,2925 @@
+/*
+ * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example
+ * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
+ *
+ * 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
+#define NANOSVG_H
+
+#ifdef __cplusplus
+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
+       NSVGImage* image;
+       image = nsvgParseFromFile("test.svg", "px", 96);
+       printf("size: %f x %f\n", image->width, image->height);
+       // Use...
+       for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) {
+               for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
+                       for (int i = 0; i < path->npts-1; i += 3) {
+                               float* p = &path->pts[i*2];
+                               drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]);
+                       }
+               }
+       }
+       // Delete
+       nsvgDelete(image);
+*/
+
+enum NSVGpaintType {
+       NSVG_PAINT_NONE = 0,
+       NSVG_PAINT_COLOR = 1,
+       NSVG_PAINT_LINEAR_GRADIENT = 2,
+       NSVG_PAINT_RADIAL_GRADIENT = 3
+};
+
+enum NSVGspreadType {
+       NSVG_SPREAD_PAD = 0,
+       NSVG_SPREAD_REFLECT = 1,
+       NSVG_SPREAD_REPEAT = 2
+};
+
+enum NSVGlineJoin {
+       NSVG_JOIN_MITER = 0,
+       NSVG_JOIN_ROUND = 1,
+       NSVG_JOIN_BEVEL = 2
+};
+
+enum NSVGlineCap {
+       NSVG_CAP_BUTT = 0,
+       NSVG_CAP_ROUND = 1,
+       NSVG_CAP_SQUARE = 2
+};
+
+enum NSVGfillRule {
+       NSVG_FILLRULE_NONZERO = 0,
+       NSVG_FILLRULE_EVENODD = 1
+};
+
+enum NSVGflags {
+       NSVG_FLAGS_VISIBLE = 0x01
+};
+
+typedef struct NSVGgradientStop {
+       unsigned int color;
+       float offset;
+} NSVGgradientStop;
+
+typedef struct NSVGgradient {
+       float xform[6];
+       char spread;
+       float fx, fy;
+       int nstops;
+       NSVGgradientStop stops[1];
+} NSVGgradient;
+
+typedef struct NSVGpaint {
+       char type;
+       union {
+               unsigned int color;
+               NSVGgradient* gradient;
+       };
+} NSVGpaint;
+
+typedef 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.
+} NSVGpath;
+
+typedef struct NSVGshape
+{
+       char id[64];                            // Optional 'id' attr of the shape or its group
+       NSVGpaint fill;                         // Fill paint
+       NSVGpaint stroke;                       // Stroke paint
+       float opacity;                          // Opacity of the shape.
+       float strokeWidth;                      // Stroke width (scaled).
+       float strokeDashOffset;         // Stroke dash offset (scaled).
+       float strokeDashArray[8];                       // Stroke dash array (scaled).
+       char strokeDashCount;                           // Number of dash values in dash array.
+       char strokeLineJoin;            // Stroke join type.
+       char strokeLineCap;                     // Stroke cap type.
+       float miterLimit;                       // Miter limit
+       char fillRule;                          // Fill rule, see NSVGfillRule.
+       unsigned char flags;            // Logical or of NSVG_FLAGS_* flags
+       float bounds[4];                        // Tight bounding box of the shape [minx,miny,maxx,maxy].
+       NSVGpath* paths;                        // Linked list of paths in the image.
+       struct NSVGshape* next;         // Pointer to next shape, or NULL if last element.
+} NSVGshape;
+
+typedef struct NSVGimage
+{
+       float width;                            // Width of the image.
+       float height;                           // Height of the image.
+       NSVGshape* shapes;                      // Linked list of shapes in the image.
+} NSVGimage;
+
+// Parses SVG file from a file, returns SVG image as paths.
+NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
+
+// Parses SVG file from a null terminated string, returns SVG image as paths.
+// Important note: changes the string.
+NSVGimage* nsvgParse(char* input, const char* units, float dpi);
+
+// Deletes list of paths.
+void nsvgDelete(NSVGimage* image);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // NANOSVG_H
+
+#ifdef NANOSVG_IMPLEMENTATION
+
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define NSVG_PI (3.14159265358979323846264338327f)
+#define NSVG_KAPPA90 (0.5522847493f)   // Length 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
+
+#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0)
+#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16))
+
+#ifdef _MSC_VER
+       #pragma warning (disable: 4996) // Switch off security warnings
+       #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings
+       #ifdef __cplusplus
+       #define NSVG_INLINE inline
+       #else
+       #define NSVG_INLINE
+       #endif
+#else
+       #define NSVG_INLINE inline
+#endif
+
+
+static int nsvg__isspace(char c)
+{
+       return strchr(" \t\n\v\f\r", c) != 0;
+}
+
+static int nsvg__isdigit(char c)
+{
+       return c >= '0' && c <= '9';
+}
+
+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; }
+
+
+// Simple XML parser
+
+#define NSVG_XML_TAG 1
+#define NSVG_XML_CONTENT 2
+#define NSVG_XML_MAX_ATTRIBS 256
+
+static void nsvg__parseContent(char* s,
+                                                          void (*contentCb)(void* ud, const char* s),
+                                                          void* ud)
+{
+       // Trim start white spaces
+       while (*s && nsvg__isspace(*s)) s++;
+       if (!*s) return;
+
+       if (contentCb)
+               (*contentCb)(ud, s);
+}
+
+static void nsvg__parseElement(char* s,
+                                                          void (*startelCb)(void* ud, const char* el, const char** attr),
+                                                          void (*endelCb)(void* ud, const char* el),
+                                                          void* ud)
+{
+       const char* attr[NSVG_XML_MAX_ATTRIBS];
+       int nattr = 0;
+       char* name;
+       int start = 0;
+       int end = 0;
+       char quote;
+
+       // Skip white space after the '<'
+       while (*s && nsvg__isspace(*s)) s++;
+
+       // Check if the tag is end tag
+       if (*s == '/') {
+               s++;
+               end = 1;
+       } else {
+               start = 1;
+       }
+
+       // Skip comments, data and preprocessor stuff.
+       if (!*s || *s == '?' || *s == '!')
+               return;
+
+       // Get tag name
+       name = s;
+       while (*s && !nsvg__isspace(*s)) s++;
+       if (*s) { *s++ = '\0'; }
+
+       // Get attribs
+       while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) {
+               char* name = NULL;
+               char* value = NULL;
+
+               // Skip white space before the attrib name
+               while (*s && nsvg__isspace(*s)) s++;
+               if (!*s) break;
+               if (*s == '/') {
+                       end = 1;
+                       break;
+               }
+               name = s;
+               // Find end of the attrib name.
+               while (*s && !nsvg__isspace(*s) && *s != '=') s++;
+               if (*s) { *s++ = '\0'; }
+               // Skip until the beginning of the value.
+               while (*s && *s != '\"' && *s != '\'') s++;
+               if (!*s) break;
+               quote = *s;
+               s++;
+               // Store value and find the end of it.
+               value = s;
+               while (*s && *s != quote) s++;
+               if (*s) { *s++ = '\0'; }
+
+               // Store only well formed attributes
+               if (name && value) {
+                       attr[nattr++] = name;
+                       attr[nattr++] = value;
+               }
+       }
+
+       // List terminator
+       attr[nattr++] = 0;
+       attr[nattr++] = 0;
+
+       // Call callbacks.
+       if (start && startelCb)
+               (*startelCb)(ud, name, attr);
+       if (end && endelCb)
+               (*endelCb)(ud, name);
+}
+
+int nsvg__parseXML(char* input,
+                                  void (*startelCb)(void* ud, const char* el, const char** attr),
+                                  void (*endelCb)(void* ud, const char* el),
+                                  void (*contentCb)(void* ud, const char* s),
+                                  void* ud)
+{
+       char* s = input;
+       char* mark = s;
+       int state = NSVG_XML_CONTENT;
+       while (*s) {
+               if (*s == '<' && state == NSVG_XML_CONTENT) {
+                       // Start of a tag
+                       *s++ = '\0';
+                       nsvg__parseContent(mark, contentCb, ud);
+                       mark = s;
+                       state = NSVG_XML_TAG;
+               } else if (*s == '>' && state == NSVG_XML_TAG) {
+                       // Start of a content or new tag.
+                       *s++ = '\0';
+                       nsvg__parseElement(mark, startelCb, endelCb, ud);
+                       mark = s;
+                       state = NSVG_XML_CONTENT;
+               } else {
+                       s++;
+               }
+       }
+
+       return 1;
+}
+
+
+/* Simple SVG parser. */
+
+#define NSVG_MAX_ATTR 128
+
+enum NSVGgradientUnits {
+       NSVG_USER_SPACE = 0,
+       NSVG_OBJECT_SPACE = 1
+};
+
+#define NSVG_MAX_DASHES 8
+
+enum NSVGunits {
+       NSVG_UNITS_USER,
+       NSVG_UNITS_PX,
+       NSVG_UNITS_PT,
+       NSVG_UNITS_PC,
+       NSVG_UNITS_MM,
+       NSVG_UNITS_CM,
+       NSVG_UNITS_IN,
+       NSVG_UNITS_PERCENT,
+       NSVG_UNITS_EM,
+       NSVG_UNITS_EX
+};
+
+typedef struct NSVGcoordinate {
+       float value;
+       int units;
+} NSVGcoordinate;
+
+typedef struct NSVGlinearData {
+       NSVGcoordinate x1, y1, x2, y2;
+} NSVGlinearData;
+
+typedef struct NSVGradialData {
+       NSVGcoordinate cx, cy, r, fx, fy;
+} NSVGradialData;
+
+typedef struct NSVGgradientData
+{
+       char id[64];
+       char ref[64];
+       char type;
+       union {
+               NSVGlinearData linear;
+               NSVGradialData radial;
+       };
+       char spread;
+       char units;
+       float xform[6];
+       int nstops;
+       NSVGgradientStop* stops;
+       struct NSVGgradientData* next;
+} NSVGgradientData;
+
+typedef struct NSVGattrib
+{
+       char id[64];
+       float xform[6];
+       unsigned int fillColor;
+       unsigned int strokeColor;
+       float opacity;
+       float fillOpacity;
+       float strokeOpacity;
+       char fillGradient[64];
+       char strokeGradient[64];
+       float strokeWidth;
+       float strokeDashOffset;
+       float strokeDashArray[NSVG_MAX_DASHES];
+       int strokeDashCount;
+       char strokeLineJoin;
+       char strokeLineCap;
+       float miterLimit;
+       char fillRule;
+       float fontSize;
+       unsigned int stopColor;
+       float stopOpacity;
+       float stopOffset;
+       char hasFill;
+       char hasStroke;
+       char visible;
+} NSVGattrib;
+
+typedef struct NSVGparser
+{
+       NSVGattrib attr[NSVG_MAX_ATTR];
+       int attrHead;
+       float* pts;
+       int npts;
+       int cpts;
+       NSVGpath* plist;
+       NSVGimage* image;
+       NSVGgradientData* gradients;
+       NSVGshape* shapesTail;
+       float viewMinx, viewMiny, viewWidth, viewHeight;
+       int alignX, alignY, alignType;
+       float dpi;
+       char pathFlag;
+       char defsFlag;
+} NSVGparser;
+
+static void nsvg__xformIdentity(float* t)
+{
+       t[0] = 1.0f; t[1] = 0.0f;
+       t[2] = 0.0f; t[3] = 1.0f;
+       t[4] = 0.0f; t[5] = 0.0f;
+}
+
+static void nsvg__xformSetTranslation(float* t, float tx, float ty)
+{
+       t[0] = 1.0f; t[1] = 0.0f;
+       t[2] = 0.0f; t[3] = 1.0f;
+       t[4] = tx; t[5] = ty;
+}
+
+static void nsvg__xformSetScale(float* t, float sx, float sy)
+{
+       t[0] = sx; t[1] = 0.0f;
+       t[2] = 0.0f; t[3] = sy;
+       t[4] = 0.0f; t[5] = 0.0f;
+}
+
+static void nsvg__xformSetSkewX(float* t, float a)
+{
+       t[0] = 1.0f; t[1] = 0.0f;
+       t[2] = tanf(a); t[3] = 1.0f;
+       t[4] = 0.0f; t[5] = 0.0f;
+}
+
+static void nsvg__xformSetSkewY(float* t, float a)
+{
+       t[0] = 1.0f; t[1] = tanf(a);
+       t[2] = 0.0f; t[3] = 1.0f;
+       t[4] = 0.0f; t[5] = 0.0f;
+}
+
+static void nsvg__xformSetRotation(float* t, float a)
+{
+       float cs = cosf(a), sn = sinf(a);
+       t[0] = cs; t[1] = sn;
+       t[2] = -sn; t[3] = cs;
+       t[4] = 0.0f; t[5] = 0.0f;
+}
+
+static void nsvg__xformMultiply(float* t, float* s)
+{
+       float t0 = t[0] * s[0] + t[1] * s[2];
+       float t2 = t[2] * s[0] + t[3] * s[2];
+       float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
+       t[1] = t[0] * s[1] + t[1] * s[3];
+       t[3] = t[2] * s[1] + t[3] * s[3];
+       t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
+       t[0] = t0;
+       t[2] = t2;
+       t[4] = t4;
+}
+
+static void nsvg__xformInverse(float* inv, float* t)
+{
+       double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1];
+       if (det > -1e-6 && det < 1e-6) {
+               nsvg__xformIdentity(t);
+               return;
+       }
+       invdet = 1.0 / det;
+       inv[0] = (float)(t[3] * invdet);
+       inv[2] = (float)(-t[2] * invdet);
+       inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
+       inv[1] = (float)(-t[1] * invdet);
+       inv[3] = (float)(t[0] * invdet);
+       inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
+}
+
+static void nsvg__xformPremultiply(float* t, float* s)
+{
+       float s2[6];
+       memcpy(s2, s, sizeof(float)*6);
+       nsvg__xformMultiply(s2, t);
+       memcpy(t, s2, sizeof(float)*6);
+}
+
+static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t)
+{
+       *dx = x*t[0] + y*t[2] + t[4];
+       *dy = x*t[1] + y*t[3] + t[5];
+}
+
+static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t)
+{
+       *dx = x*t[0] + y*t[2];
+       *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)
+{
+       double 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 NSVGparser* nsvg__createParser()
+{
+       NSVGparser* p;
+       p = (NSVGparser*)malloc(sizeof(NSVGparser));
+       if (p == NULL) goto error;
+       memset(p, 0, sizeof(NSVGparser));
+
+       p->image = (NSVGimage*)malloc(sizeof(NSVGimage));
+       if (p->image == NULL) goto error;
+       memset(p->image, 0, sizeof(NSVGimage));
+
+       // Init style
+       nsvg__xformIdentity(p->attr[0].xform);
+       memset(p->attr[0].id, 0, sizeof p->attr[0].id);
+       p->attr[0].fillColor = NSVG_RGB(0,0,0);
+       p->attr[0].strokeColor = NSVG_RGB(0,0,0);
+       p->attr[0].opacity = 1;
+       p->attr[0].fillOpacity = 1;
+       p->attr[0].strokeOpacity = 1;
+       p->attr[0].stopOpacity = 1;
+       p->attr[0].strokeWidth = 1;
+       p->attr[0].strokeLineJoin = NSVG_JOIN_MITER;
+       p->attr[0].strokeLineCap = NSVG_CAP_BUTT;
+       p->attr[0].miterLimit = 4;
+       p->attr[0].fillRule = NSVG_FILLRULE_NONZERO;
+       p->attr[0].hasFill = 1;
+       p->attr[0].visible = 1;
+
+       return p;
+
+error:
+       if (p) {
+               if (p->image) free(p->image);
+               free(p);
+       }
+       return NULL;
+}
+
+static void nsvg__deletePaths(NSVGpath* path)
+{
+       while (path) {
+               NSVGpath *next = path->next;
+               if (path->pts != NULL)
+                       free(path->pts);
+               free(path);
+               path = next;
+       }
+}
+
+static void nsvg__deletePaint(NSVGpaint* paint)
+{
+       if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT)
+               free(paint->gradient);
+}
+
+static void nsvg__deleteGradientData(NSVGgradientData* grad)
+{
+       NSVGgradientData* next;
+       while (grad != NULL) {
+               next = grad->next;
+               free(grad->stops);
+               free(grad);
+               grad = next;
+       }
+}
+
+static void nsvg__deleteParser(NSVGparser* p)
+{
+       if (p != NULL) {
+               nsvg__deletePaths(p->plist);
+               nsvg__deleteGradientData(p->gradients);
+               nsvgDelete(p->image);
+               free(p->pts);
+               free(p);
+       }
+}
+
+static void nsvg__resetPath(NSVGparser* p)
+{
+       p->npts = 0;
+}
+
+static void nsvg__addPoint(NSVGparser* p, float x, float y)
+{
+       if (p->npts+1 > p->cpts) {
+               p->cpts = p->cpts ? p->cpts*2 : 8;
+               p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float));
+               if (!p->pts) return;
+       }
+       p->pts[p->npts*2+0] = x;
+       p->pts[p->npts*2+1] = y;
+       p->npts++;
+}
+
+static void nsvg__moveTo(NSVGparser* p, float x, float y)
+{
+       if (p->npts > 0) {
+               p->pts[(p->npts-1)*2+0] = x;
+               p->pts[(p->npts-1)*2+1] = y;
+       } else {
+               nsvg__addPoint(p, x, y);
+       }
+}
+
+static void nsvg__lineTo(NSVGparser* p, float x, float y)
+{
+       float px,py, dx,dy;
+       if (p->npts > 0) {
+               px = p->pts[(p->npts-1)*2+0];
+               py = p->pts[(p->npts-1)*2+1];
+               dx = x - px;
+               dy = y - py;
+               nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f);
+               nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f);
+               nsvg__addPoint(p, x, y);
+       }
+}
+
+static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
+{
+       nsvg__addPoint(p, cpx1, cpy1);
+       nsvg__addPoint(p, cpx2, cpy2);
+       nsvg__addPoint(p, x, y);
+}
+
+static NSVGattrib* nsvg__getAttr(NSVGparser* p)
+{
+       return &p->attr[p->attrHead];
+}
+
+static void nsvg__pushAttr(NSVGparser* p)
+{
+       if (p->attrHead < NSVG_MAX_ATTR-1) {
+               p->attrHead++;
+               memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib));
+       }
+}
+
+static void nsvg__popAttr(NSVGparser* p)
+{
+       if (p->attrHead > 0)
+               p->attrHead--;
+}
+
+static float nsvg__actualOrigX(NSVGparser* p)
+{
+       return p->viewMinx;
+}
+
+static float nsvg__actualOrigY(NSVGparser* p)
+{
+       return p->viewMiny;
+}
+
+static float nsvg__actualWidth(NSVGparser* p)
+{
+       return p->viewWidth;
+}
+
+static float nsvg__actualHeight(NSVGparser* p)
+{
+       return p->viewHeight;
+}
+
+static float nsvg__actualLength(NSVGparser* p)
+{
+       float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p);
+       return sqrtf(w*w + h*h) / sqrtf(2.0f);
+}
+
+static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length)
+{
+       NSVGattrib* attr = nsvg__getAttr(p);
+       switch (c.units) {
+               case NSVG_UNITS_USER:           return c.value;
+               case NSVG_UNITS_PX:                     return c.value;
+               case NSVG_UNITS_PT:                     return c.value / 72.0f * p->dpi;
+               case NSVG_UNITS_PC:                     return c.value / 6.0f * p->dpi;
+               case NSVG_UNITS_MM:                     return c.value / 25.4f * p->dpi;
+               case NSVG_UNITS_CM:                     return c.value / 2.54f * p->dpi;
+               case NSVG_UNITS_IN:                     return c.value * p->dpi;
+               case NSVG_UNITS_EM:                     return c.value * attr->fontSize;
+               case NSVG_UNITS_EX:                     return c.value * attr->fontSize * 0.52f; // x-height of Helvetica.
+               case NSVG_UNITS_PERCENT:        return orig + c.value / 100.0f * length;
+               default:                                        return c.value;
+       }
+       return c.value;
+}
+
+static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
+{
+       NSVGgradientData* grad = p->gradients;
+       while (grad) {
+               if (strcmp(grad->id, id) == 0)
+                       return grad;
+               grad = grad->next;
+       }
+       return NULL;
+}
+
+static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType)
+{
+       NSVGattrib* attr = nsvg__getAttr(p);
+       NSVGgradientData* data = NULL;
+       NSVGgradientData* ref = NULL;
+       NSVGgradientStop* stops = NULL;
+       NSVGgradient* grad;
+       float ox, oy, sw, sh, sl;
+       int nstops = 0;
+
+       data = nsvg__findGradientData(p, id);
+       if (data == NULL) return NULL;
+
+       // TODO: use ref to fill in all unset values too.
+       ref = data;
+       while (ref != NULL) {
+               if (stops == NULL && ref->stops != NULL) {
+                       stops = ref->stops;
+                       nstops = ref->nstops;
+                       break;
+               }
+               ref = nsvg__findGradientData(p, ref->ref);
+       }
+       if (stops == NULL) return NULL;
+
+       grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1));
+       if (grad == NULL) return NULL;
+
+       // The shape width and height.
+       if (data->units == NSVG_OBJECT_SPACE) {
+               ox = localBounds[0];
+               oy = localBounds[1];
+               sw = localBounds[2] - localBounds[0];
+               sh = localBounds[3] - localBounds[1];
+       } else {
+               ox = nsvg__actualOrigX(p);
+               oy = nsvg__actualOrigY(p);
+               sw = nsvg__actualWidth(p);
+               sh = nsvg__actualHeight(p);
+       }
+       sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f);
+
+       if (data->type == NSVG_PAINT_LINEAR_GRADIENT) {
+               float x1, y1, x2, y2, dx, dy;
+               x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw);
+               y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh);
+               x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw);
+               y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh);
+               // Calculate transform aligned to the line
+               dx = x2 - x1;
+               dy = y2 - y1;
+               grad->xform[0] = dy; grad->xform[1] = -dx;
+               grad->xform[2] = dx; grad->xform[3] = dy;
+               grad->xform[4] = x1; grad->xform[5] = y1;
+       } else {
+               float cx, cy, fx, fy, r;
+               cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw);
+               cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh);
+               fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw);
+               fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh);
+               r = nsvg__convertToPixels(p, data->radial.r, 0, sl);
+               // Calculate transform aligned to the circle
+               grad->xform[0] = r; grad->xform[1] = 0;
+               grad->xform[2] = 0; grad->xform[3] = r;
+               grad->xform[4] = cx; grad->xform[5] = cy;
+               grad->fx = fx / r;
+               grad->fy = fy / r;
+       }
+
+       nsvg__xformMultiply(grad->xform, data->xform);
+       nsvg__xformMultiply(grad->xform, attr->xform);
+
+       grad->spread = data->spread;
+       memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop));
+       grad->nstops = nstops;
+
+       *paintType = data->type;
+
+       return grad;
+}
+
+static float nsvg__getAverageScale(float* t)
+{
+       float sx = sqrtf(t[0]*t[0] + t[2]*t[2]);
+       float sy = sqrtf(t[1]*t[1] + t[3]*t[3]);
+       return (sx + sy) * 0.5f;
+}
+
+static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform)
+{
+       NSVGpath* path;
+       float curve[4*2], curveBounds[4];
+       int i, first = 1;
+       for (path = shape->paths; path != NULL; path = path->next) {
+               nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform);
+               for (i = 0; i < path->npts-1; i += 3) {
+                       nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform);
+                       nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform);
+                       nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform);
+                       nsvg__curveBounds(curveBounds, curve);
+                       if (first) {
+                               bounds[0] = curveBounds[0];
+                               bounds[1] = curveBounds[1];
+                               bounds[2] = curveBounds[2];
+                               bounds[3] = curveBounds[3];
+                               first = 0;
+                       } else {
+                               bounds[0] = nsvg__minf(bounds[0], curveBounds[0]);
+                               bounds[1] = nsvg__minf(bounds[1], curveBounds[1]);
+                               bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]);
+                               bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]);
+                       }
+                       curve[0] = curve[6];
+                       curve[1] = curve[7];
+               }
+       }
+}
+
+static void nsvg__addShape(NSVGparser* p)
+{
+       NSVGattrib* attr = nsvg__getAttr(p);
+       float scale = 1.0f;
+       NSVGshape* shape;
+       NSVGpath* path;
+       int i;
+
+       if (p->plist == NULL)
+               return;
+
+       shape = (NSVGshape*)malloc(sizeof(NSVGshape));
+       if (shape == NULL) goto error;
+       memset(shape, 0, sizeof(NSVGshape));
+
+       memcpy(shape->id, attr->id, sizeof shape->id);
+       scale = nsvg__getAverageScale(attr->xform);
+       shape->strokeWidth = attr->strokeWidth * scale;
+       shape->strokeDashOffset = attr->strokeDashOffset * scale;
+       shape->strokeDashCount = (char)attr->strokeDashCount;
+       for (i = 0; i < attr->strokeDashCount; i++)
+               shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
+       shape->strokeLineJoin = attr->strokeLineJoin;
+       shape->strokeLineCap = attr->strokeLineCap;
+       shape->miterLimit = attr->miterLimit;
+       shape->fillRule = attr->fillRule;
+       shape->opacity = attr->opacity;
+
+       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]);
+       }
+
+       // Set fill
+       if (attr->hasFill == 0) {
+               shape->fill.type = NSVG_PAINT_NONE;
+       } else if (attr->hasFill == 1) {
+               shape->fill.type = NSVG_PAINT_COLOR;
+               shape->fill.color = attr->fillColor;
+               shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24;
+       } else if (attr->hasFill == 2) {
+               float inv[6], localBounds[4];
+               nsvg__xformInverse(inv, attr->xform);
+               nsvg__getLocalBounds(localBounds, shape, inv);
+               shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type);
+               if (shape->fill.gradient == NULL) {
+                       shape->fill.type = NSVG_PAINT_NONE;
+               }
+       }
+
+       // Set stroke
+       if (attr->hasStroke == 0) {
+               shape->stroke.type = NSVG_PAINT_NONE;
+       } else if (attr->hasStroke == 1) {
+               shape->stroke.type = NSVG_PAINT_COLOR;
+               shape->stroke.color = attr->strokeColor;
+               shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24;
+       } else if (attr->hasStroke == 2) {
+               float inv[6], localBounds[4];
+               nsvg__xformInverse(inv, attr->xform);
+               nsvg__getLocalBounds(localBounds, shape, inv);
+               shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type);
+               if (shape->stroke.gradient == NULL)
+                       shape->stroke.type = NSVG_PAINT_NONE;
+       }
+
+       // Set flags
+       shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00);
+
+       // Add to tail
+       if (p->image->shapes == NULL)
+               p->image->shapes = shape;
+       else
+               p->shapesTail->next = shape;
+       p->shapesTail = shape;
+
+       return;
+
+error:
+       if (shape) free(shape);
+}
+
+static void nsvg__addPath(NSVGparser* p, char closed)
+{
+       NSVGattrib* attr = nsvg__getAttr(p);
+       NSVGpath* path = NULL;
+       float bounds[4];
+       float* curve;
+       int i;
+
+       if (p->npts < 4)
+               return;
+
+       if (closed)
+               nsvg__lineTo(p, p->pts[0], p->pts[1]);
+
+       path = (NSVGpath*)malloc(sizeof(NSVGpath));
+       if (path == NULL) goto error;
+       memset(path, 0, sizeof(NSVGpath));
+
+       path->pts = (float*)malloc(p->npts*2*sizeof(float));
+       if (path->pts == NULL) goto error;
+       path->closed = closed;
+       path->npts = p->npts;
+
+       // Transform path.
+       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;
+
+       return;
+
+error:
+       if (path != NULL) {
+               if (path->pts != NULL) free(path->pts);
+               free(path);
+       }
+}
+
+// We roll our own string to float because the std library one uses locale and messes things up.
+static double nsvg__atof(const char* s)
+{
+       char* cur = (char*)s;
+       char* end = NULL;
+       double res = 0.0, sign = 1.0;
+       long long intPart = 0, fracPart = 0;
+       char hasIntPart = 0, hasFracPart = 0;
+
+       // Parse optional sign
+       if (*cur == '+') {
+               cur++;
+       } else if (*cur == '-') {
+               sign = -1;
+               cur++;
+       }
+
+       // Parse integer part
+       if (nsvg__isdigit(*cur)) {
+               // Parse digit sequence
+               intPart = (double)strtoll(cur, &end, 10);
+               if (cur != end) {
+                       res = (double)intPart;
+                       hasIntPart = 1;
+                       cur = end;
+               }
+       }
+
+       // Parse fractional part.
+       if (*cur == '.') {
+               cur++; // Skip '.'
+               if (nsvg__isdigit(*cur)) {
+                       // Parse digit sequence
+                       fracPart = strtoll(cur, &end, 10);
+                       if (cur != end) {
+                               res += (double)fracPart / pow(10.0, (double)(end - cur));
+                               hasFracPart = 1;
+                               cur = end;
+                       }
+               }
+       }
+
+       // A valid number should have integer or fractional part.
+       if (!hasIntPart && !hasFracPart)
+               return 0.0;
+
+       // Parse optional exponent
+       if (*cur == 'e' || *cur == 'E') {
+               int expPart = 0;
+               cur++; // skip 'E'
+               expPart = strtol(cur, &end, 10); // Parse digit sequence with sign
+               if (cur != end) {
+                       res *= pow(10.0, (double)expPart);
+               }
+       }
+
+       return res * sign;
+}
+
+
+static const char* nsvg__parseNumber(const char* s, char* it, const int size)
+{
+       const int last = size-1;
+       int i = 0;
+
+       // sign
+       if (*s == '-' || *s == '+') {
+               if (i < last) it[i++] = *s;
+               s++;
+       }
+       // integer part
+       while (*s && nsvg__isdigit(*s)) {
+               if (i < last) it[i++] = *s;
+               s++;
+       }
+       if (*s == '.') {
+               // decimal point
+               if (i < last) it[i++] = *s;
+               s++;
+               // fraction part
+               while (*s && nsvg__isdigit(*s)) {
+                       if (i < last) it[i++] = *s;
+                       s++;
+               }
+       }
+       // exponent
+       if (*s == 'e' || *s == 'E') {
+               if (i < last) it[i++] = *s;
+               s++;
+               if (*s == '-' || *s == '+') {
+                       if (i < last) it[i++] = *s;
+                       s++;
+               }
+               while (*s && nsvg__isdigit(*s)) {
+                       if (i < last) it[i++] = *s;
+                       s++;
+               }
+       }
+       it[i] = '\0';
+
+       return s;
+}
+
+static const char* nsvg__getNextPathItem(const char* s, char* it)
+{
+       it[0] = '\0';
+       // Skip white spaces and commas
+       while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
+       if (!*s) return s;
+       if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
+               s = nsvg__parseNumber(s, it, 64);
+       } else {
+               // Parse command
+               it[0] = *s++;
+               it[1] = '\0';
+               return s;
+       }
+
+       return s;
+}
+
+static unsigned int nsvg__parseColorHex(const char* str)
+{
+       unsigned int c = 0, r = 0, g = 0, b = 0;
+       int n = 0;
+       str++; // skip #
+       // Calculate number of characters.
+       while(str[n] && !nsvg__isspace(str[n]))
+               n++;
+       if (n == 6) {
+               sscanf(str, "%x", &c);
+       } else if (n == 3) {
+               sscanf(str, "%x", &c);
+               c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8);
+               c |= c<<4;
+       }
+       r = (c >> 16) & 0xff;
+       g = (c >> 8) & 0xff;
+       b = c & 0xff;
+       return NSVG_RGB(r,g,b);
+}
+
+static unsigned int nsvg__parseColorRGB(const char* str)
+{
+       int r = -1, g = -1, b = -1;
+       char s1[32]="", s2[32]="";
+       sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b);
+       if (strchr(s1, '%')) {
+               return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100);
+       } else {
+               return NSVG_RGB(r,g,b);
+       }
+}
+
+typedef struct NSVGNamedColor {
+       const char* name;
+       unsigned int color;
+} NSVGNamedColor;
+
+NSVGNamedColor nsvg__colors[] = {
+
+       { "red", NSVG_RGB(255, 0, 0) },
+       { "green", NSVG_RGB( 0, 128, 0) },
+       { "blue", NSVG_RGB( 0, 0, 255) },
+       { "yellow", NSVG_RGB(255, 255, 0) },
+       { "cyan", NSVG_RGB( 0, 255, 255) },
+       { "magenta", NSVG_RGB(255, 0, 255) },
+       { "black", NSVG_RGB( 0, 0, 0) },
+       { "grey", NSVG_RGB(128, 128, 128) },
+       { "gray", NSVG_RGB(128, 128, 128) },
+       { "white", NSVG_RGB(255, 255, 255) },
+
+#ifdef NANOSVG_ALL_COLOR_KEYWORDS
+       { "aliceblue", NSVG_RGB(240, 248, 255) },
+       { "antiquewhite", NSVG_RGB(250, 235, 215) },
+       { "aqua", NSVG_RGB( 0, 255, 255) },
+       { "aquamarine", NSVG_RGB(127, 255, 212) },
+       { "azure", NSVG_RGB(240, 255, 255) },
+       { "beige", NSVG_RGB(245, 245, 220) },
+       { "bisque", NSVG_RGB(255, 228, 196) },
+       { "blanchedalmond", NSVG_RGB(255, 235, 205) },
+       { "blueviolet", NSVG_RGB(138, 43, 226) },
+       { "brown", NSVG_RGB(165, 42, 42) },
+       { "burlywood", NSVG_RGB(222, 184, 135) },
+       { "cadetblue", NSVG_RGB( 95, 158, 160) },
+       { "chartreuse", NSVG_RGB(127, 255, 0) },
+       { "chocolate", NSVG_RGB(210, 105, 30) },
+       { "coral", NSVG_RGB(255, 127, 80) },
+       { "cornflowerblue", NSVG_RGB(100, 149, 237) },
+       { "cornsilk", NSVG_RGB(255, 248, 220) },
+       { "crimson", NSVG_RGB(220, 20, 60) },
+       { "darkblue", NSVG_RGB( 0, 0, 139) },
+       { "darkcyan", NSVG_RGB( 0, 139, 139) },
+       { "darkgoldenrod", NSVG_RGB(184, 134, 11) },
+       { "darkgray", NSVG_RGB(169, 169, 169) },
+       { "darkgreen", NSVG_RGB( 0, 100, 0) },
+       { "darkgrey", NSVG_RGB(169, 169, 169) },
+       { "darkkhaki", NSVG_RGB(189, 183, 107) },
+       { "darkmagenta", NSVG_RGB(139, 0, 139) },
+       { "darkolivegreen", NSVG_RGB( 85, 107, 47) },
+       { "darkorange", NSVG_RGB(255, 140, 0) },
+       { "darkorchid", NSVG_RGB(153, 50, 204) },
+       { "darkred", NSVG_RGB(139, 0, 0) },
+       { "darksalmon", NSVG_RGB(233, 150, 122) },
+       { "darkseagreen", NSVG_RGB(143, 188, 143) },
+       { "darkslateblue", NSVG_RGB( 72, 61, 139) },
+       { "darkslategray", NSVG_RGB( 47, 79, 79) },
+       { "darkslategrey", NSVG_RGB( 47, 79, 79) },
+       { "darkturquoise", NSVG_RGB( 0, 206, 209) },
+       { "darkviolet", NSVG_RGB(148, 0, 211) },
+       { "deeppink", NSVG_RGB(255, 20, 147) },
+       { "deepskyblue", NSVG_RGB( 0, 191, 255) },
+       { "dimgray", NSVG_RGB(105, 105, 105) },
+       { "dimgrey", NSVG_RGB(105, 105, 105) },
+       { "dodgerblue", NSVG_RGB( 30, 144, 255) },
+       { "firebrick", NSVG_RGB(178, 34, 34) },
+       { "floralwhite", NSVG_RGB(255, 250, 240) },
+       { "forestgreen", NSVG_RGB( 34, 139, 34) },
+       { "fuchsia", NSVG_RGB(255, 0, 255) },
+       { "gainsboro", NSVG_RGB(220, 220, 220) },
+       { "ghostwhite", NSVG_RGB(248, 248, 255) },
+       { "gold", NSVG_RGB(255, 215, 0) },
+       { "goldenrod", NSVG_RGB(218, 165, 32) },
+       { "greenyellow", NSVG_RGB(173, 255, 47) },
+       { "honeydew", NSVG_RGB(240, 255, 240) },
+       { "hotpink", NSVG_RGB(255, 105, 180) },
+       { "indianred", NSVG_RGB(205, 92, 92) },
+       { "indigo", NSVG_RGB( 75, 0, 130) },
+       { "ivory", NSVG_RGB(255, 255, 240) },
+       { "khaki", NSVG_RGB(240, 230, 140) },
+       { "lavender", NSVG_RGB(230, 230, 250) },
+       { "lavenderblush", NSVG_RGB(255, 240, 245) },
+       { "lawngreen", NSVG_RGB(124, 252, 0) },
+       { "lemonchiffon", NSVG_RGB(255, 250, 205) },
+       { "lightblue", NSVG_RGB(173, 216, 230) },
+       { "lightcoral", NSVG_RGB(240, 128, 128) },
+       { "lightcyan", NSVG_RGB(224, 255, 255) },
+       { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) },
+       { "lightgray", NSVG_RGB(211, 211, 211) },
+       { "lightgreen", NSVG_RGB(144, 238, 144) },
+       { "lightgrey", NSVG_RGB(211, 211, 211) },
+       { "lightpink", NSVG_RGB(255, 182, 193) },
+       { "lightsalmon", NSVG_RGB(255, 160, 122) },
+       { "lightseagreen", NSVG_RGB( 32, 178, 170) },
+       { "lightskyblue", NSVG_RGB(135, 206, 250) },
+       { "lightslategray", NSVG_RGB(119, 136, 153) },
+       { "lightslategrey", NSVG_RGB(119, 136, 153) },
+       { "lightsteelblue", NSVG_RGB(176, 196, 222) },
+       { "lightyellow", NSVG_RGB(255, 255, 224) },
+       { "lime", NSVG_RGB( 0, 255, 0) },
+       { "limegreen", NSVG_RGB( 50, 205, 50) },
+       { "linen", NSVG_RGB(250, 240, 230) },
+       { "maroon", NSVG_RGB(128, 0, 0) },
+       { "mediumaquamarine", NSVG_RGB(102, 205, 170) },
+       { "mediumblue", NSVG_RGB( 0, 0, 205) },
+       { "mediumorchid", NSVG_RGB(186, 85, 211) },
+       { "mediumpurple", NSVG_RGB(147, 112, 219) },
+       { "mediumseagreen", NSVG_RGB( 60, 179, 113) },
+       { "mediumslateblue", NSVG_RGB(123, 104, 238) },
+       { "mediumspringgreen", NSVG_RGB( 0, 250, 154) },
+       { "mediumturquoise", NSVG_RGB( 72, 209, 204) },
+       { "mediumvioletred", NSVG_RGB(199, 21, 133) },
+       { "midnightblue", NSVG_RGB( 25, 25, 112) },
+       { "mintcream", NSVG_RGB(245, 255, 250) },
+       { "mistyrose", NSVG_RGB(255, 228, 225) },
+       { "moccasin", NSVG_RGB(255, 228, 181) },
+       { "navajowhite", NSVG_RGB(255, 222, 173) },
+       { "navy", NSVG_RGB( 0, 0, 128) },
+       { "oldlace", NSVG_RGB(253, 245, 230) },
+       { "olive", NSVG_RGB(128, 128, 0) },
+       { "olivedrab", NSVG_RGB(107, 142, 35) },
+       { "orange", NSVG_RGB(255, 165, 0) },
+       { "orangered", NSVG_RGB(255, 69, 0) },
+       { "orchid", NSVG_RGB(218, 112, 214) },
+       { "palegoldenrod", NSVG_RGB(238, 232, 170) },
+       { "palegreen", NSVG_RGB(152, 251, 152) },
+       { "paleturquoise", NSVG_RGB(175, 238, 238) },
+       { "palevioletred", NSVG_RGB(219, 112, 147) },
+       { "papayawhip", NSVG_RGB(255, 239, 213) },
+       { "peachpuff", NSVG_RGB(255, 218, 185) },
+       { "peru", NSVG_RGB(205, 133, 63) },
+       { "pink", NSVG_RGB(255, 192, 203) },
+       { "plum", NSVG_RGB(221, 160, 221) },
+       { "powderblue", NSVG_RGB(176, 224, 230) },
+       { "purple", NSVG_RGB(128, 0, 128) },
+       { "rosybrown", NSVG_RGB(188, 143, 143) },
+       { "royalblue", NSVG_RGB( 65, 105, 225) },
+       { "saddlebrown", NSVG_RGB(139, 69, 19) },
+       { "salmon", NSVG_RGB(250, 128, 114) },
+       { "sandybrown", NSVG_RGB(244, 164, 96) },
+       { "seagreen", NSVG_RGB( 46, 139, 87) },
+       { "seashell", NSVG_RGB(255, 245, 238) },
+       { "sienna", NSVG_RGB(160, 82, 45) },
+       { "silver", NSVG_RGB(192, 192, 192) },
+       { "skyblue", NSVG_RGB(135, 206, 235) },
+       { "slateblue", NSVG_RGB(106, 90, 205) },
+       { "slategray", NSVG_RGB(112, 128, 144) },
+       { "slategrey", NSVG_RGB(112, 128, 144) },
+       { "snow", NSVG_RGB(255, 250, 250) },
+       { "springgreen", NSVG_RGB( 0, 255, 127) },
+       { "steelblue", NSVG_RGB( 70, 130, 180) },
+       { "tan", NSVG_RGB(210, 180, 140) },
+       { "teal", NSVG_RGB( 0, 128, 128) },
+       { "thistle", NSVG_RGB(216, 191, 216) },
+       { "tomato", NSVG_RGB(255, 99, 71) },
+       { "turquoise", NSVG_RGB( 64, 224, 208) },
+       { "violet", NSVG_RGB(238, 130, 238) },
+       { "wheat", NSVG_RGB(245, 222, 179) },
+       { "whitesmoke", NSVG_RGB(245, 245, 245) },
+       { "yellowgreen", NSVG_RGB(154, 205, 50) },
+#endif
+};
+
+static unsigned int nsvg__parseColorName(const char* str)
+{
+       int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor);
+
+       for (i = 0; i < ncolors; i++) {
+               if (strcmp(nsvg__colors[i].name, str) == 0) {
+                       return nsvg__colors[i].color;
+               }
+       }
+
+       return NSVG_RGB(128, 128, 128);
+}
+
+static unsigned int nsvg__parseColor(const char* str)
+{
+       size_t len = 0;
+       while(*str == ' ') ++str;
+       len = strlen(str);
+       if (len >= 1 && *str == '#')
+               return nsvg__parseColorHex(str);
+       else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(')
+               return nsvg__parseColorRGB(str);
+       return nsvg__parseColorName(str);
+}
+
+static float nsvg__parseOpacity(const char* str)
+{
+       float val = 0;
+       sscanf(str, "%f", &val);
+       if (val < 0.0f) val = 0.0f;
+       if (val > 1.0f) val = 1.0f;
+       return val;
+}
+
+static float nsvg__parseMiterLimit(const char* str)
+{
+       float val = 0;
+       sscanf(str, "%f", &val);
+       if (val < 0.0f) val = 0.0f;
+       return val;
+}
+
+static int nsvg__parseUnits(const char* units)
+{
+       if (units[0] == 'p' && units[1] == 'x')
+               return NSVG_UNITS_PX;
+       else if (units[0] == 'p' && units[1] == 't')
+               return NSVG_UNITS_PT;
+       else if (units[0] == 'p' && units[1] == 'c')
+               return NSVG_UNITS_PC;
+       else if (units[0] == 'm' && units[1] == 'm')
+               return NSVG_UNITS_MM;
+       else if (units[0] == 'c' && units[1] == 'm')
+               return NSVG_UNITS_CM;
+       else if (units[0] == 'i' && units[1] == 'n')
+               return NSVG_UNITS_IN;
+       else if (units[0] == '%')
+               return NSVG_UNITS_PERCENT;
+       else if (units[0] == 'e' && units[1] == 'm')
+               return NSVG_UNITS_EM;
+       else if (units[0] == 'e' && units[1] == 'x')
+               return NSVG_UNITS_EX;
+       return NSVG_UNITS_USER;
+}
+
+static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str)
+{
+       NSVGcoordinate coord = {0, NSVG_UNITS_USER};
+       char units[32]="";
+       sscanf(str, "%f%31s", &coord.value, units);
+       coord.units = nsvg__parseUnits(units);
+       return coord;
+}
+
+static NSVGcoordinate nsvg__coord(float v, int units)
+{
+       NSVGcoordinate coord = {v, units};
+       return coord;
+}
+
+static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length)
+{
+       NSVGcoordinate coord = nsvg__parseCoordinateRaw(str);
+       return nsvg__convertToPixels(p, coord, orig, length);
+}
+
+static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na)
+{
+       const char* end;
+       const char* ptr;
+       char it[64];
+
+       *na = 0;
+       ptr = str;
+       while (*ptr && *ptr != '(') ++ptr;
+       if (*ptr == 0)
+               return 1;
+       end = ptr;
+       while (*end && *end != ')') ++end;
+       if (*end == 0)
+               return 1;
+
+       while (ptr < end) {
+               if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) {
+                       if (*na >= maxNa) return 0;
+                       ptr = nsvg__parseNumber(ptr, it, 64);
+                       args[(*na)++] = (float)nsvg__atof(it);
+               } else {
+                       ++ptr;
+               }
+       }
+       return (int)(end - str);
+}
+
+
+static int nsvg__parseMatrix(float* xform, const char* str)
+{
+       float t[6];
+       int na = 0;
+       int len = nsvg__parseTransformArgs(str, t, 6, &na);
+       if (na != 6) return len;
+       memcpy(xform, t, sizeof(float)*6);
+       return len;
+}
+
+static int nsvg__parseTranslate(float* xform, const char* str)
+{
+       float args[2];
+       float t[6];
+       int na = 0;
+       int len = nsvg__parseTransformArgs(str, args, 2, &na);
+       if (na == 1) args[1] = 0.0;
+
+       nsvg__xformSetTranslation(t, args[0], args[1]);
+       memcpy(xform, t, sizeof(float)*6);
+       return len;
+}
+
+static int nsvg__parseScale(float* xform, const char* str)
+{
+       float args[2];
+       int na = 0;
+       float t[6];
+       int len = nsvg__parseTransformArgs(str, args, 2, &na);
+       if (na == 1) args[1] = args[0];
+       nsvg__xformSetScale(t, args[0], args[1]);
+       memcpy(xform, t, sizeof(float)*6);
+       return len;
+}
+
+static int nsvg__parseSkewX(float* xform, const char* str)
+{
+       float args[1];
+       int na = 0;
+       float t[6];
+       int len = nsvg__parseTransformArgs(str, args, 1, &na);
+       nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI);
+       memcpy(xform, t, sizeof(float)*6);
+       return len;
+}
+
+static int nsvg__parseSkewY(float* xform, const char* str)
+{
+       float args[1];
+       int na = 0;
+       float t[6];
+       int len = nsvg__parseTransformArgs(str, args, 1, &na);
+       nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI);
+       memcpy(xform, t, sizeof(float)*6);
+       return len;
+}
+
+static int nsvg__parseRotate(float* xform, const char* str)
+{
+       float args[3];
+       int na = 0;
+       float m[6];
+       float t[6];
+       int len = nsvg__parseTransformArgs(str, args, 3, &na);
+       if (na == 1)
+               args[1] = args[2] = 0.0f;
+       nsvg__xformIdentity(m);
+
+       if (na > 1) {
+               nsvg__xformSetTranslation(t, -args[1], -args[2]);
+               nsvg__xformMultiply(m, t);
+       }
+
+       nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI);
+       nsvg__xformMultiply(m, t);
+
+       if (na > 1) {
+               nsvg__xformSetTranslation(t, args[1], args[2]);
+               nsvg__xformMultiply(m, t);
+       }
+
+       memcpy(xform, m, sizeof(float)*6);
+
+       return len;
+}
+
+static void nsvg__parseTransform(float* xform, const char* str)
+{
+       float t[6];
+       nsvg__xformIdentity(xform);
+       while (*str)
+       {
+               if (strncmp(str, "matrix", 6) == 0)
+                       str += nsvg__parseMatrix(t, str);
+               else if (strncmp(str, "translate", 9) == 0)
+                       str += nsvg__parseTranslate(t, str);
+               else if (strncmp(str, "scale", 5) == 0)
+                       str += nsvg__parseScale(t, str);
+               else if (strncmp(str, "rotate", 6) == 0)
+                       str += nsvg__parseRotate(t, str);
+               else if (strncmp(str, "skewX", 5) == 0)
+                       str += nsvg__parseSkewX(t, str);
+               else if (strncmp(str, "skewY", 5) == 0)
+                       str += nsvg__parseSkewY(t, str);
+               else{
+                       ++str;
+                       continue;
+               }
+
+               nsvg__xformPremultiply(xform, t);
+       }
+}
+
+static void nsvg__parseUrl(char* id, const char* str)
+{
+       int i = 0;
+       str += 4; // "url(";
+       if (*str == '#')
+               str++;
+       while (i < 63 && *str != ')') {
+               id[i] = *str++;
+               i++;
+       }
+       id[i] = '\0';
+}
+
+static char nsvg__parseLineCap(const char* str)
+{
+       if (strcmp(str, "butt") == 0)
+               return NSVG_CAP_BUTT;
+       else if (strcmp(str, "round") == 0)
+               return NSVG_CAP_ROUND;
+       else if (strcmp(str, "square") == 0)
+               return NSVG_CAP_SQUARE;
+       // TODO: handle inherit.
+       return NSVG_CAP_BUTT;
+}
+
+static char nsvg__parseLineJoin(const char* str)
+{
+       if (strcmp(str, "miter") == 0)
+               return NSVG_JOIN_MITER;
+       else if (strcmp(str, "round") == 0)
+               return NSVG_JOIN_ROUND;
+       else if (strcmp(str, "bevel") == 0)
+               return NSVG_JOIN_BEVEL;
+       // TODO: handle inherit.
+       return NSVG_JOIN_MITER;
+}
+
+static char nsvg__parseFillRule(const char* str)
+{
+       if (strcmp(str, "nonzero") == 0)
+               return NSVG_FILLRULE_NONZERO;
+       else if (strcmp(str, "evenodd") == 0)
+               return NSVG_FILLRULE_EVENODD;
+       // TODO: handle inherit.
+       return NSVG_FILLRULE_NONZERO;
+}
+
+static const char* nsvg__getNextDashItem(const char* s, char* it)
+{
+       int n = 0;
+       it[0] = '\0';
+       // Skip white spaces and commas
+       while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
+       // Advance until whitespace, comma or end.
+       while (*s && (!nsvg__isspace(*s) && *s != ',')) {
+               if (n < 63)
+                       it[n++] = *s;
+               s++;
+       }
+       it[n++] = '\0';
+       return s;
+}
+
+static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray)
+{
+       char item[64];
+       int count = 0, i;
+       float sum = 0.0f;
+
+       // Handle "none"
+       if (str[0] == 'n')
+               return 0;
+
+       // Parse dashes
+       while (*str) {
+               str = nsvg__getNextDashItem(str, item);
+               if (!*item) break;
+               if (count < NSVG_MAX_DASHES)
+                       strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p)));
+       }
+
+       for (i = 0; i < count; i++)
+               sum += strokeDashArray[i];
+       if (sum <= 1e-6f)
+               count = 0;
+
+       return count;
+}
+
+static void nsvg__parseStyle(NSVGparser* p, const char* str);
+
+static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
+{
+       float xform[6];
+       NSVGattrib* attr = nsvg__getAttr(p);
+       if (!attr) return 0;
+
+       if (strcmp(name, "style") == 0) {
+               nsvg__parseStyle(p, value);
+       } else if (strcmp(name, "display") == 0) {
+               if (strcmp(value, "none") == 0)
+                       attr->visible = 0;
+               // Don't reset ->visible on display:inline, one display:none hides the whole subtree
+
+       } else if (strcmp(name, "fill") == 0) {
+               if (strcmp(value, "none") == 0) {
+                       attr->hasFill = 0;
+               } else if (strncmp(value, "url(", 4) == 0) {
+                       attr->hasFill = 2;
+                       nsvg__parseUrl(attr->fillGradient, value);
+               } else {
+                       attr->hasFill = 1;
+                       attr->fillColor = nsvg__parseColor(value);
+               }
+       } else if (strcmp(name, "opacity") == 0) {
+               attr->opacity = nsvg__parseOpacity(value);
+       } else if (strcmp(name, "fill-opacity") == 0) {
+               attr->fillOpacity = nsvg__parseOpacity(value);
+       } else if (strcmp(name, "stroke") == 0) {
+               if (strcmp(value, "none") == 0) {
+                       attr->hasStroke = 0;
+               } else if (strncmp(value, "url(", 4) == 0) {
+                       attr->hasStroke = 2;
+                       nsvg__parseUrl(attr->strokeGradient, value);
+               } else {
+                       attr->hasStroke = 1;
+                       attr->strokeColor = nsvg__parseColor(value);
+               }
+       } else if (strcmp(name, "stroke-width") == 0) {
+               attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
+       } else if (strcmp(name, "stroke-dasharray") == 0) {
+               attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray);
+       } else if (strcmp(name, "stroke-dashoffset") == 0) {
+               attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
+       } else if (strcmp(name, "stroke-opacity") == 0) {
+               attr->strokeOpacity = nsvg__parseOpacity(value);
+       } else if (strcmp(name, "stroke-linecap") == 0) {
+               attr->strokeLineCap = nsvg__parseLineCap(value);
+       } else if (strcmp(name, "stroke-linejoin") == 0) {
+               attr->strokeLineJoin = nsvg__parseLineJoin(value);
+       } else if (strcmp(name, "stroke-miterlimit") == 0) {
+               attr->miterLimit = nsvg__parseMiterLimit(value);
+       } else if (strcmp(name, "fill-rule") == 0) {
+               attr->fillRule = nsvg__parseFillRule(value);
+       } else if (strcmp(name, "font-size") == 0) {
+               attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
+       } else if (strcmp(name, "transform") == 0) {
+               nsvg__parseTransform(xform, value);
+               nsvg__xformPremultiply(attr->xform, xform);
+       } else if (strcmp(name, "stop-color") == 0) {
+               attr->stopColor = nsvg__parseColor(value);
+       } else if (strcmp(name, "stop-opacity") == 0) {
+               attr->stopOpacity = nsvg__parseOpacity(value);
+       } else if (strcmp(name, "offset") == 0) {
+               attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f);
+       } else if (strcmp(name, "id") == 0) {
+               strncpy(attr->id, value, 63);
+               attr->id[63] = '\0';
+       } else {
+               return 0;
+       }
+       return 1;
+}
+
+static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end)
+{
+       const char* str;
+       const char* val;
+       char name[512];
+       char value[512];
+       int n;
+
+       str = start;
+       while (str < end && *str != ':') ++str;
+
+       val = str;
+
+       // Right Trim
+       while (str > start &&  (*str == ':' || nsvg__isspace(*str))) --str;
+       ++str;
+
+       n = (int)(str - start);
+       if (n > 511) n = 511;
+       if (n) memcpy(name, start, n);
+       name[n] = 0;
+
+       while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val;
+
+       n = (int)(end - val);
+       if (n > 511) n = 511;
+       if (n) memcpy(value, val, n);
+       value[n] = 0;
+
+       return nsvg__parseAttr(p, name, value);
+}
+
+static void nsvg__parseStyle(NSVGparser* p, const char* str)
+{
+       const char* start;
+       const char* end;
+
+       while (*str) {
+               // Left Trim
+               while(*str && nsvg__isspace(*str)) ++str;
+               start = str;
+               while(*str && *str != ';') ++str;
+               end = str;
+
+               // Right Trim
+               while (end > start &&  (*end == ';' || nsvg__isspace(*end))) --end;
+               ++end;
+
+               nsvg__parseNameValue(p, start, end);
+               if (*str) ++str;
+       }
+}
+
+static void nsvg__parseAttribs(NSVGparser* p, const char** attr)
+{
+       int i;
+       for (i = 0; attr[i]; i += 2)
+       {
+               if (strcmp(attr[i], "style") == 0)
+                       nsvg__parseStyle(p, attr[i + 1]);
+               else
+                       nsvg__parseAttr(p, attr[i], attr[i + 1]);
+       }
+}
+
+static int nsvg__getArgsPerElement(char cmd)
+{
+       switch (cmd) {
+               case 'v':
+               case 'V':
+               case 'h':
+               case 'H':
+                       return 1;
+               case 'm':
+               case 'M':
+               case 'l':
+               case 'L':
+               case 't':
+               case 'T':
+                       return 2;
+               case 'q':
+               case 'Q':
+               case 's':
+               case 'S':
+                       return 4;
+               case 'c':
+               case 'C':
+                       return 6;
+               case 'a':
+               case 'A':
+                       return 7;
+       }
+       return 0;
+}
+
+static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
+{
+       if (rel) {
+               *cpx += args[0];
+               *cpy += args[1];
+       } else {
+               *cpx = args[0];
+               *cpy = args[1];
+       }
+       nsvg__moveTo(p, *cpx, *cpy);
+}
+
+static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
+{
+       if (rel) {
+               *cpx += args[0];
+               *cpy += args[1];
+       } else {
+               *cpx = args[0];
+               *cpy = args[1];
+       }
+       nsvg__lineTo(p, *cpx, *cpy);
+}
+
+static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
+{
+       if (rel)
+               *cpx += args[0];
+       else
+               *cpx = args[0];
+       nsvg__lineTo(p, *cpx, *cpy);
+}
+
+static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
+{
+       if (rel)
+               *cpy += args[0];
+       else
+               *cpy = args[0];
+       nsvg__lineTo(p, *cpx, *cpy);
+}
+
+static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy,
+                                                                float* cpx2, float* cpy2, float* args, int rel)
+{
+       float x2, y2, cx1, cy1, cx2, cy2;
+
+       if (rel) {
+               cx1 = *cpx + args[0];
+               cy1 = *cpy + args[1];
+               cx2 = *cpx + args[2];
+               cy2 = *cpy + args[3];
+               x2 = *cpx + args[4];
+               y2 = *cpy + args[5];
+       } else {
+               cx1 = args[0];
+               cy1 = args[1];
+               cx2 = args[2];
+               cy2 = args[3];
+               x2 = args[4];
+               y2 = args[5];
+       }
+
+       nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
+
+       *cpx2 = cx2;
+       *cpy2 = cy2;
+       *cpx = x2;
+       *cpy = y2;
+}
+
+static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy,
+                                                                         float* cpx2, float* cpy2, float* args, int rel)
+{
+       float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
+
+       x1 = *cpx;
+       y1 = *cpy;
+       if (rel) {
+               cx2 = *cpx + args[0];
+               cy2 = *cpy + args[1];
+               x2 = *cpx + args[2];
+               y2 = *cpy + args[3];
+       } else {
+               cx2 = args[0];
+               cy2 = args[1];
+               x2 = args[2];
+               y2 = args[3];
+       }
+
+       cx1 = 2*x1 - *cpx2;
+       cy1 = 2*y1 - *cpy2;
+
+       nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
+
+       *cpx2 = cx2;
+       *cpy2 = cy2;
+       *cpx = x2;
+       *cpy = y2;
+}
+
+static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy,
+                                                               float* cpx2, float* cpy2, float* args, int rel)
+{
+       float x1, y1, x2, y2, cx, cy;
+       float cx1, cy1, cx2, cy2;
+
+       x1 = *cpx;
+       y1 = *cpy;
+       if (rel) {
+               cx = *cpx + args[0];
+               cy = *cpy + args[1];
+               x2 = *cpx + args[2];
+               y2 = *cpy + args[3];
+       } else {
+               cx = args[0];
+               cy = args[1];
+               x2 = args[2];
+               y2 = args[3];
+       }
+
+       // Convert to cubic bezier
+       cx1 = x1 + 2.0f/3.0f*(cx - x1);
+       cy1 = y1 + 2.0f/3.0f*(cy - y1);
+       cx2 = x2 + 2.0f/3.0f*(cx - x2);
+       cy2 = y2 + 2.0f/3.0f*(cy - y2);
+
+       nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
+
+       *cpx2 = cx;
+       *cpy2 = cy;
+       *cpx = x2;
+       *cpy = y2;
+}
+
+static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy,
+                                                                        float* cpx2, float* cpy2, float* args, int rel)
+{
+       float x1, y1, x2, y2, cx, cy;
+       float cx1, cy1, cx2, cy2;
+
+       x1 = *cpx;
+       y1 = *cpy;
+       if (rel) {
+               x2 = *cpx + args[0];
+               y2 = *cpy + args[1];
+       } else {
+               x2 = args[0];
+               y2 = args[1];
+       }
+
+       cx = 2*x1 - *cpx2;
+       cy = 2*y1 - *cpy2;
+
+       // Convert to cubix bezier
+       cx1 = x1 + 2.0f/3.0f*(cx - x1);
+       cy1 = y1 + 2.0f/3.0f*(cy - y1);
+       cx2 = x2 + 2.0f/3.0f*(cx - x2);
+       cy2 = y2 + 2.0f/3.0f*(cy - y2);
+
+       nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
+
+       *cpx2 = cx;
+       *cpy2 = cy;
+       *cpx = x2;
+       *cpy = y2;
+}
+
+static float nsvg__sqr(float x) { return x*x; }
+static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); }
+
+static float nsvg__vecrat(float ux, float uy, float vx, float vy)
+{
+       return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy));
+}
+
+static float nsvg__vecang(float ux, float uy, float vx, float vy)
+{
+       float r = nsvg__vecrat(ux,uy, vx,vy);
+       if (r < -1.0f) r = -1.0f;
+       if (r > 1.0f) r = 1.0f;
+       return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r);
+}
+
+static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
+{
+       // Ported from canvg (https://code.google.com/p/canvg/)
+       float rx, ry, rotx;
+       float x1, y1, x2, y2, cx, cy, dx, dy, d;
+       float x1p, y1p, cxp, cyp, s, sa, sb;
+       float ux, uy, vx, vy, a1, da;
+       float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6];
+       float sinrx, cosrx;
+       int fa, fs;
+       int i, ndivs;
+       float hda, kappa;
+
+       rx = fabsf(args[0]);                            // y radius
+       ry = fabsf(args[1]);                            // x radius
+       rotx = args[2] / 180.0f * NSVG_PI;              // x rotation angle
+       fa = fabsf(args[3]) > 1e-6 ? 1 : 0;     // Large arc
+       fs = fabsf(args[4]) > 1e-6 ? 1 : 0;     // Sweep direction
+       x1 = *cpx;                                                      // start point
+       y1 = *cpy;
+       if (rel) {                                                      // end point
+               x2 = *cpx + args[5];
+               y2 = *cpy + args[6];
+       } else {
+               x2 = args[5];
+               y2 = args[6];
+       }
+
+       dx = x1 - x2;
+       dy = y1 - y2;
+       d = sqrtf(dx*dx + dy*dy);
+       if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) {
+               // The arc degenerates to a line
+               nsvg__lineTo(p, x2, y2);
+               *cpx = x2;
+               *cpy = y2;
+               return;
+       }
+
+       sinrx = sinf(rotx);
+       cosrx = cosf(rotx);
+
+       // Convert to center point parameterization.
+       // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+       // 1) Compute x1', y1'
+       x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f;
+       y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f;
+       d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry);
+       if (d > 1) {
+               d = sqrtf(d);
+               rx *= d;
+               ry *= d;
+       }
+       // 2) Compute cx', cy'
+       s = 0.0f;
+       sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p);
+       sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p);
+       if (sa < 0.0f) sa = 0.0f;
+       if (sb > 0.0f)
+               s = sqrtf(sa / sb);
+       if (fa == fs)
+               s = -s;
+       cxp = s * rx * y1p / ry;
+       cyp = s * -ry * x1p / rx;
+
+       // 3) Compute cx,cy from cx',cy'
+       cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp;
+       cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp;
+
+       // 4) Calculate theta1, and delta theta.
+       ux = (x1p - cxp) / rx;
+       uy = (y1p - cyp) / ry;
+       vx = (-x1p - cxp) / rx;
+       vy = (-y1p - cyp) / ry;
+       a1 = nsvg__vecang(1.0f,0.0f, ux,uy);    // Initial angle
+       da = nsvg__vecang(ux,uy, vx,vy);                // Delta angle
+
+//     if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI;
+//     if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0;
+
+       if (fs == 0 && da > 0)
+               da -= 2 * NSVG_PI;
+       else if (fs == 1 && da < 0)
+               da += 2 * NSVG_PI;
+
+       // Approximate the arc using cubic spline segments.
+       t[0] = cosrx; t[1] = sinrx;
+       t[2] = -sinrx; t[3] = cosrx;
+       t[4] = cx; t[5] = cy;
+
+       // Split arc into max 90 degree segments.
+       // The loop assumes an iteration per end point (including start and end), this +1.
+       ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f);
+       hda = (da / (float)ndivs) / 2.0f;
+       kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
+       if (da < 0.0f)
+               kappa = -kappa;
+
+       for (i = 0; i <= ndivs; i++) {
+               a = a1 + da * ((float)i/(float)ndivs);
+               dx = cosf(a);
+               dy = sinf(a);
+               nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position
+               nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent
+               if (i > 0)
+                       nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y);
+               px = x;
+               py = y;
+               ptanx = tanx;
+               ptany = tany;
+       }
+
+       *cpx = x2;
+       *cpy = y2;
+}
+
+static void nsvg__parsePath(NSVGparser* p, const char** attr)
+{
+       const char* s = NULL;
+       char cmd = '\0';
+       float args[10];
+       int nargs;
+       int rargs = 0;
+       float cpx, cpy, cpx2, cpy2;
+       const char* tmp[4];
+       char closedFlag;
+       int i;
+       char item[64];
+
+       for (i = 0; attr[i]; i += 2) {
+               if (strcmp(attr[i], "d") == 0) {
+                       s = attr[i + 1];
+               } else {
+                       tmp[0] = attr[i];
+                       tmp[1] = attr[i + 1];
+                       tmp[2] = 0;
+                       tmp[3] = 0;
+                       nsvg__parseAttribs(p, tmp);
+               }
+       }
+
+       if (s) {
+               nsvg__resetPath(p);
+               cpx = 0; cpy = 0;
+               cpx2 = 0; cpy2 = 0;
+               closedFlag = 0;
+               nargs = 0;
+
+               while (*s) {
+                       s = nsvg__getNextPathItem(s, item);
+                       if (!*item) break;
+                       if (nsvg__isnum(item[0])) {
+                               if (nargs < 10)
+                                       args[nargs++] = (float)nsvg__atof(item);
+                               if (nargs >= rargs) {
+                                       switch (cmd) {
+                                               case 'm':
+                                               case 'M':
+                                                       nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0);
+                                                       // Moveto can be followed by multiple coordinate pairs,
+                                                       // which should be treated as linetos.
+                                                       cmd = (cmd == 'm') ? 'l' : 'L';
+                                                       rargs = nsvg__getArgsPerElement(cmd);
+                                                       cpx2 = cpx; cpy2 = cpy;
+                                                       break;
+                                               case 'l':
+                                               case 'L':
+                                                       nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
+                                                       cpx2 = cpx; cpy2 = cpy;
+                                                       break;
+                                               case 'H':
+                                               case 'h':
+                                                       nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
+                                                       cpx2 = cpx; cpy2 = cpy;
+                                                       break;
+                                               case 'V':
+                                               case 'v':
+                                                       nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
+                                                       cpx2 = cpx; cpy2 = cpy;
+                                                       break;
+                                               case 'C':
+                                               case 'c':
+                                                       nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
+                                                       break;
+                                               case 'S':
+                                               case 's':
+                                                       nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
+                                                       break;
+                                               case 'Q':
+                                               case 'q':
+                                                       nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
+                                                       break;
+                                               case 'T':
+                                               case 't':
+                                                       nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0);
+                                                       break;
+                                               case 'A':
+                                               case 'a':
+                                                       nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
+                                                       cpx2 = cpx; cpy2 = cpy;
+                                                       break;
+                                               default:
+                                                       if (nargs >= 2) {
+                                                               cpx = args[nargs-2];
+                                                               cpy = args[nargs-1];
+                                                               cpx2 = cpx; cpy2 = cpy;
+                                                       }
+                                                       break;
+                                       }
+                                       nargs = 0;
+                               }
+                       } else {
+                               cmd = item[0];
+                               rargs = nsvg__getArgsPerElement(cmd);
+                               if (cmd == 'M' || cmd == 'm') {
+                                       // Commit path.
+                                       if (p->npts > 0)
+                                               nsvg__addPath(p, closedFlag);
+                                       // Start new subpath.
+                                       nsvg__resetPath(p);
+                                       closedFlag = 0;
+                                       nargs = 0;
+                               } else if (cmd == 'Z' || cmd == 'z') {
+                                       closedFlag = 1;
+                                       // Commit path.
+                                       if (p->npts > 0) {
+                                               // Move current point to first point
+                                               cpx = p->pts[0];
+                                               cpy = p->pts[1];
+                                               cpx2 = cpx; cpy2 = cpy;
+                                               nsvg__addPath(p, closedFlag);
+                                       }
+                                       // Start new subpath.
+                                       nsvg__resetPath(p);
+                                       nsvg__moveTo(p, cpx, cpy);
+                                       closedFlag = 0;
+                                       nargs = 0;
+                               }
+                       }
+               }
+               // Commit path.
+               if (p->npts)
+                       nsvg__addPath(p, closedFlag);
+       }
+
+       nsvg__addShape(p);
+}
+
+static void nsvg__parseRect(NSVGparser* p, const char** attr)
+{
+       float x = 0.0f;
+       float y = 0.0f;
+       float w = 0.0f;
+       float h = 0.0f;
+       float rx = -1.0f; // marks not set
+       float ry = -1.0f;
+       int i;
+
+       for (i = 0; attr[i]; i += 2) {
+               if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+                       if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
+                       if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
+                       if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p));
+                       if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p));
+                       if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
+                       if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
+               }
+       }
+
+       if (rx < 0.0f && ry > 0.0f) rx = ry;
+       if (ry < 0.0f && rx > 0.0f) ry = rx;
+       if (rx < 0.0f) rx = 0.0f;
+       if (ry < 0.0f) ry = 0.0f;
+       if (rx > w/2.0f) rx = w/2.0f;
+       if (ry > h/2.0f) ry = h/2.0f;
+
+       if (w != 0.0f && h != 0.0f) {
+               nsvg__resetPath(p);
+
+               if (rx < 0.00001f || ry < 0.0001f) {
+                       nsvg__moveTo(p, x, y);
+                       nsvg__lineTo(p, x+w, y);
+                       nsvg__lineTo(p, x+w, y+h);
+                       nsvg__lineTo(p, x, y+h);
+               } else {
+                       // Rounded rectangle
+                       nsvg__moveTo(p, x+rx, y);
+                       nsvg__lineTo(p, x+w-rx, y);
+                       nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry);
+                       nsvg__lineTo(p, x+w, y+h-ry);
+                       nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h);
+                       nsvg__lineTo(p, x+rx, y+h);
+                       nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry);
+                       nsvg__lineTo(p, x, y+ry);
+                       nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y);
+               }
+
+               nsvg__addPath(p, 1);
+
+               nsvg__addShape(p);
+       }
+}
+
+static void nsvg__parseCircle(NSVGparser* p, const char** attr)
+{
+       float cx = 0.0f;
+       float cy = 0.0f;
+       float r = 0.0f;
+       int i;
+
+       for (i = 0; attr[i]; i += 2) {
+               if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+                       if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
+                       if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
+                       if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p)));
+               }
+       }
+
+       if (r > 0.0f) {
+               nsvg__resetPath(p);
+
+               nsvg__moveTo(p, cx+r, cy);
+               nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r);
+               nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy);
+               nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r);
+               nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy);
+
+               nsvg__addPath(p, 1);
+
+               nsvg__addShape(p);
+       }
+}
+
+static void nsvg__parseEllipse(NSVGparser* p, const char** attr)
+{
+       float cx = 0.0f;
+       float cy = 0.0f;
+       float rx = 0.0f;
+       float ry = 0.0f;
+       int i;
+
+       for (i = 0; attr[i]; i += 2) {
+               if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+                       if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
+                       if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
+                       if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
+                       if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
+               }
+       }
+
+       if (rx > 0.0f && ry > 0.0f) {
+
+               nsvg__resetPath(p);
+
+               nsvg__moveTo(p, cx+rx, cy);
+               nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry);
+               nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy);
+               nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry);
+               nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy);
+
+               nsvg__addPath(p, 1);
+
+               nsvg__addShape(p);
+       }
+}
+
+static void nsvg__parseLine(NSVGparser* p, const char** attr)
+{
+       float x1 = 0.0;
+       float y1 = 0.0;
+       float x2 = 0.0;
+       float y2 = 0.0;
+       int i;
+
+       for (i = 0; attr[i]; i += 2) {
+               if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+                       if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
+                       if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
+                       if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
+                       if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
+               }
+       }
+
+       nsvg__resetPath(p);
+
+       nsvg__moveTo(p, x1, y1);
+       nsvg__lineTo(p, x2, y2);
+
+       nsvg__addPath(p, 0);
+
+       nsvg__addShape(p);
+}
+
+static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag)
+{
+       int i;
+       const char* s;
+       float args[2];
+       int nargs, npts = 0;
+       char item[64];
+
+       nsvg__resetPath(p);
+
+       for (i = 0; attr[i]; i += 2) {
+               if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+                       if (strcmp(attr[i], "points") == 0) {
+                               s = attr[i + 1];
+                               nargs = 0;
+                               while (*s) {
+                                       s = nsvg__getNextPathItem(s, item);
+                                       args[nargs++] = (float)nsvg__atof(item);
+                                       if (nargs >= 2) {
+                                               if (npts == 0)
+                                                       nsvg__moveTo(p, args[0], args[1]);
+                                               else
+                                                       nsvg__lineTo(p, args[0], args[1]);
+                                               nargs = 0;
+                                               npts++;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       nsvg__addPath(p, (char)closeFlag);
+
+       nsvg__addShape(p);
+}
+
+static void nsvg__parseSVG(NSVGparser* p, const char** attr)
+{
+       int i;
+       for (i = 0; attr[i]; i += 2) {
+               if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+                       if (strcmp(attr[i], "width") == 0) {
+                               p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
+                       } else if (strcmp(attr[i], "height") == 0) {
+                               p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
+                       } 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;
+                               }
+                       }
+               }
+       }
+}
+
+static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type)
+{
+       int i;
+       NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData));
+       if (grad == NULL) return;
+       memset(grad, 0, sizeof(NSVGgradientData));
+       grad->units = NSVG_OBJECT_SPACE;
+       grad->type = type;
+       if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) {
+               grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
+               grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
+               grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT);
+               grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
+       } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) {
+               grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
+               grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
+               grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
+       }
+
+       nsvg__xformIdentity(grad->xform);
+
+       for (i = 0; attr[i]; i += 2) {
+               if (strcmp(attr[i], "id") == 0) {
+                       strncpy(grad->id, attr[i+1], 63);
+                       grad->id[63] = '\0';
+               } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+                       if (strcmp(attr[i], "gradientUnits") == 0) {
+                               if (strcmp(attr[i+1], "objectBoundingBox") == 0)
+                                       grad->units = NSVG_OBJECT_SPACE;
+                               else
+                                       grad->units = NSVG_USER_SPACE;
+                       } else if (strcmp(attr[i], "gradientTransform") == 0) {
+                               nsvg__parseTransform(grad->xform, attr[i + 1]);
+                       } else if (strcmp(attr[i], "cx") == 0) {
+                               grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]);
+                       } else if (strcmp(attr[i], "cy") == 0) {
+                               grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]);
+                       } else if (strcmp(attr[i], "r") == 0) {
+                               grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]);
+                       } else if (strcmp(attr[i], "fx") == 0) {
+                               grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]);
+                       } else if (strcmp(attr[i], "fy") == 0) {
+                               grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]);
+                       } else if (strcmp(attr[i], "x1") == 0) {
+                               grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]);
+                       } else if (strcmp(attr[i], "y1") == 0) {
+                               grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]);
+                       } else if (strcmp(attr[i], "x2") == 0) {
+                               grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]);
+                       } else if (strcmp(attr[i], "y2") == 0) {
+                               grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]);
+                       } else if (strcmp(attr[i], "spreadMethod") == 0) {
+                               if (strcmp(attr[i+1], "pad") == 0)
+                                       grad->spread = NSVG_SPREAD_PAD;
+                               else if (strcmp(attr[i+1], "reflect") == 0)
+                                       grad->spread = NSVG_SPREAD_REFLECT;
+                               else if (strcmp(attr[i+1], "repeat") == 0)
+                                       grad->spread = NSVG_SPREAD_REPEAT;
+                       } else if (strcmp(attr[i], "xlink:href") == 0) {
+                               const char *href = attr[i+1];
+                               strncpy(grad->ref, href+1, 62);
+                               grad->ref[62] = '\0';
+                       }
+               }
+       }
+
+       grad->next = p->gradients;
+       p->gradients = grad;
+}
+
+static void nsvg__parseGradientStop(NSVGparser* p, const char** attr)
+{
+       NSVGattrib* curAttr = nsvg__getAttr(p);
+       NSVGgradientData* grad;
+       NSVGgradientStop* stop;
+       int i, idx;
+
+       curAttr->stopOffset = 0;
+       curAttr->stopColor = 0;
+       curAttr->stopOpacity = 1.0f;
+
+       for (i = 0; attr[i]; i += 2) {
+               nsvg__parseAttr(p, attr[i], attr[i + 1]);
+       }
+
+       // Add stop to the last gradient.
+       grad = p->gradients;
+       if (grad == NULL) return;
+
+       grad->nstops++;
+       grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops);
+       if (grad->stops == NULL) return;
+
+       // Insert
+       idx = grad->nstops-1;
+       for (i = 0; i < grad->nstops-1; i++) {
+               if (curAttr->stopOffset < grad->stops[i].offset) {
+                       idx = i;
+                       break;
+               }
+       }
+       if (idx != grad->nstops-1) {
+               for (i = grad->nstops-1; i > idx; i--)
+                       grad->stops[i] = grad->stops[i-1];
+       }
+
+       stop = &grad->stops[idx];
+       stop->color = curAttr->stopColor;
+       stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24;
+       stop->offset = curAttr->stopOffset;
+}
+
+static void nsvg__startElement(void* ud, const char* el, const char** attr)
+{
+       NSVGparser* p = (NSVGparser*)ud;
+
+       if (p->defsFlag) {
+               // Skip everything but gradients in defs
+               if (strcmp(el, "linearGradient") == 0) {
+                       nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
+               } else if (strcmp(el, "radialGradient") == 0) {
+                       nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
+               } else if (strcmp(el, "stop") == 0) {
+                       nsvg__parseGradientStop(p, attr);
+               }
+               return;
+       }
+
+       if (strcmp(el, "g") == 0) {
+               nsvg__pushAttr(p);
+               nsvg__parseAttribs(p, attr);
+       } else if (strcmp(el, "path") == 0) {
+               if (p->pathFlag)        // Do not allow nested paths.
+                       return;
+               nsvg__pushAttr(p);
+               nsvg__parsePath(p, attr);
+               nsvg__popAttr(p);
+       } else if (strcmp(el, "rect") == 0) {
+               nsvg__pushAttr(p);
+               nsvg__parseRect(p, attr);
+               nsvg__popAttr(p);
+       } else if (strcmp(el, "circle") == 0) {
+               nsvg__pushAttr(p);
+               nsvg__parseCircle(p, attr);
+               nsvg__popAttr(p);
+       } else if (strcmp(el, "ellipse") == 0) {
+               nsvg__pushAttr(p);
+               nsvg__parseEllipse(p, attr);
+               nsvg__popAttr(p);
+       } else if (strcmp(el, "line") == 0)  {
+               nsvg__pushAttr(p);
+               nsvg__parseLine(p, attr);
+               nsvg__popAttr(p);
+       } else if (strcmp(el, "polyline") == 0)  {
+               nsvg__pushAttr(p);
+               nsvg__parsePoly(p, attr, 0);
+               nsvg__popAttr(p);
+       } else if (strcmp(el, "polygon") == 0)  {
+               nsvg__pushAttr(p);
+               nsvg__parsePoly(p, attr, 1);
+               nsvg__popAttr(p);
+       } else  if (strcmp(el, "linearGradient") == 0) {
+               nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
+       } else if (strcmp(el, "radialGradient") == 0) {
+               nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
+       } else if (strcmp(el, "stop") == 0) {
+               nsvg__parseGradientStop(p, attr);
+       } else if (strcmp(el, "defs") == 0) {
+               p->defsFlag = 1;
+       } else if (strcmp(el, "svg") == 0) {
+               nsvg__parseSVG(p, attr);
+       }
+}
+
+static void nsvg__endElement(void* ud, const char* el)
+{
+       NSVGparser* p = (NSVGparser*)ud;
+
+       if (strcmp(el, "g") == 0) {
+               nsvg__popAttr(p);
+       } else if (strcmp(el, "path") == 0) {
+               p->pathFlag = 0;
+       } else if (strcmp(el, "defs") == 0) {
+               p->defsFlag = 0;
+       }
+}
+
+static void nsvg__content(void* ud, const char* s)
+{
+       NSVG_NOTUSED(ud);
+       NSVG_NOTUSED(s);
+       // empty
+}
+
+static void nsvg__imageBounds(NSVGparser* p, float* bounds)
+{
+       NSVGshape* shape;
+       shape = p->image->shapes;
+       if (shape == NULL) {
+               bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0;
+               return;
+       }
+       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]);
+       }
+}
+
+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__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy)
+{
+       float t[6];
+       nsvg__xformSetTranslation(t, tx, ty);
+       nsvg__xformMultiply (grad->xform, t);
+
+       nsvg__xformSetScale(t, sx, sy);
+       nsvg__xformMultiply (grad->xform, t);
+}
+
+static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
+{
+       NSVGshape* shape;
+       NSVGpath* path;
+       float tx, ty, sx, sy, us, bounds[4], t[6], avgs;
+       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->viewMinx = bounds[0];
+                       p->viewWidth = bounds[2] - bounds[0];
+               }
+       }
+       if (p->viewHeight == 0) {
+               if (p->image->height > 0) {
+                       p->viewHeight = p->image->height;
+               } else {
+                       p->viewMiny = bounds[1];
+                       p->viewHeight = bounds[3] - bounds[1];
+               }
+       }
+       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;
+       // Unit scaling
+       us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f);
+
+       // 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;
+       avgs = (sx+sy) / 2.0f;
+       for (shape = p->image->shapes; shape != NULL; shape = shape->next) {
+               shape->bounds[0] = (shape->bounds[0] + tx) * sx;
+               shape->bounds[1] = (shape->bounds[1] + ty) * sy;
+               shape->bounds[2] = (shape->bounds[2] + tx) * sx;
+               shape->bounds[3] = (shape->bounds[3] + ty) * sy;
+               for (path = shape->paths; path != NULL; path = path->next) {
+                       path->bounds[0] = (path->bounds[0] + tx) * sx;
+                       path->bounds[1] = (path->bounds[1] + ty) * sy;
+                       path->bounds[2] = (path->bounds[2] + tx) * sx;
+                       path->bounds[3] = (path->bounds[3] + ty) * 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;
+                       }
+               }
+
+               if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) {
+                       nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy);
+                       memcpy(t, shape->fill.gradient->xform, sizeof(float)*6);
+                       nsvg__xformInverse(shape->fill.gradient->xform, t);
+               }
+               if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) {
+                       nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy);
+                       memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6);
+                       nsvg__xformInverse(shape->stroke.gradient->xform, t);
+               }
+
+               shape->strokeWidth *= avgs;
+               shape->strokeDashOffset *= avgs;
+               for (i = 0; i < shape->strokeDashCount; i++)
+                       shape->strokeDashArray[i] *= avgs;
+       }
+}
+
+NSVGimage* nsvgParse(char* input, const char* units, float dpi)
+{
+       NSVGparser* p;
+       NSVGimage* ret = 0;
+
+       p = nsvg__createParser();
+       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;
+
+       nsvg__deleteParser(p);
+
+       return ret;
+}
+
+NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
+{
+       FILE* fp = NULL;
+       size_t size;
+       char* data = NULL;
+       NSVGimage* image = NULL;
+
+       fp = fopen(filename, "rb");
+       if (!fp) goto error;
+       fseek(fp, 0, SEEK_END);
+       size = ftell(fp);
+       fseek(fp, 0, SEEK_SET);
+       data = (char*)malloc(size+1);
+       if (data == NULL) goto error;
+       if (fread(data, 1, size, fp) != size) goto error;
+       data[size] = '\0';      // Must be null terminated.
+       fclose(fp);
+       image = nsvgParse(data, units, dpi);
+       free(data);
+
+       return image;
+
+error:
+       if (fp) fclose(fp);
+       if (data) free(data);
+       if (image) nsvgDelete(image);
+       return NULL;
+}
+
+void nsvgDelete(NSVGimage* image)
+{
+       NSVGshape *snext, *shape;
+       if (image == NULL) return;
+       shape = image->shapes;
+       while (shape != NULL) {
+               snext = shape->next;
+               nsvg__deletePaths(shape->paths);
+               nsvg__deletePaint(&shape->fill);
+               nsvg__deletePaint(&shape->stroke);
+               free(shape);
+               shape = snext;
+       }
+       free(image);
+}
+
+#endif
diff --git a/tests/common/test.c b/tests/common/test.c
new file mode 100644 (file)
index 0000000..19acddc
--- /dev/null
@@ -0,0 +1,117 @@
+#include "test.h"
+
+float panX = 0.f;
+float panY = 0.f;
+float lastX = 0.f;
+float lastY = 0.f;
+float zoom = 1.0f;
+bool mouseDown = false;
+
+VkvgDevice device = NULL;
+VkvgSurface surf = NULL;
+
+static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
+    if (action != GLFW_PRESS)
+        return;
+    switch (key) {
+    case GLFW_KEY_ESCAPE :
+        glfwSetWindowShouldClose(window, GLFW_TRUE);
+        break;
+    }
+}
+static void char_callback (GLFWwindow* window, uint32_t c){}
+static void mouse_move_callback(GLFWwindow* window, double x, double y){
+    if (mouseDown) {
+        panX += ((float)x-lastX);
+        panY += ((float)y-lastY);
+    }
+    lastX = (float)x;
+    lastY = (float)y;
+}
+static void scroll_callback(GLFWwindow* window, double x, double y){
+    if (y<0.f)
+        zoom *= 0.5f;
+    else
+        zoom *= 2.0f;
+}
+static void mouse_button_callback(GLFWwindow* window, int but, int state, int modif){
+    if (but != GLFW_MOUSE_BUTTON_1)
+        return;
+    if (state == GLFW_TRUE)
+        mouseDown = true;
+    else
+        mouseDown = false;
+}
+
+double time_diff(struct timeval x , struct timeval y)
+{
+    double x_ms , y_ms , diff;
+
+    x_ms = (double)x.tv_sec*1000000 + (double)x.tv_usec;
+    y_ms = (double)y.tv_sec*1000000 + (double)y.tv_usec;
+
+    diff = (double)y_ms - (double)x_ms;
+
+    return diff;
+}
+
+void randomize_color (VkvgContext ctx) {
+    vkvg_set_source_rgba(ctx,
+        (float)rand()/RAND_MAX,
+        (float)rand()/RAND_MAX,
+        (float)rand()/RAND_MAX,
+        (float)rand()/RAND_MAX
+    );
+}
+
+void perform_test (void(*testfunc)(void),uint width, uint height) {
+    //dumpLayerExts();
+
+    vk_engine_t* e = vkengine_create (VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, VK_PRESENT_MODE_MAILBOX_KHR, width, height);
+    VkhPresenter r = e->renderer;
+    vkengine_set_key_callback (e, key_callback);
+    vkengine_set_mouse_but_callback(e, mouse_button_callback);
+    vkengine_set_cursor_pos_callback(e, mouse_move_callback);
+    vkengine_set_scroll_callback(e, scroll_callback);
+
+    bool deferredResolve = false;
+
+    device  = vkvg_device_create_multisample(vkh_app_get_inst(e->app), r->dev->phy, r->dev->dev, r->qFam, 0, VK_SAMPLE_COUNT_4_BIT, deferredResolve);
+    surf    = vkvg_surface_create(device, width, height);
+
+    vkvg_surface_clear(surf);
+
+    vkh_presenter_build_blit_cmd (r, vkvg_surface_get_vk_image(surf), width, height);
+
+    struct timeval before , after;
+    double frameTime = 0, frameTimeAccum = 0, frameCount = 0;
+
+    while (!vkengine_should_close (e)) {
+        glfwPollEvents();
+
+        gettimeofday(&before , NULL);
+
+        testfunc();
+
+        if (deferredResolve)
+            vkvg_multisample_surface_resolve(surf);
+        if (!vkh_presenter_draw (r))
+            vkh_presenter_build_blit_cmd (r, vkvg_surface_get_vk_image(surf), width, height);
+
+        gettimeofday(&after , NULL);
+
+        frameTimeAccum += time_diff(before , after);
+        frameCount++;
+    }
+
+    frameTime = frameTimeAccum / frameCount;
+    printf ("frame (µs): %.0lf\nfps: %lf\n", frameTime, floor(1000000 / frameTime));
+
+    vkDeviceWaitIdle(e->dev->dev);
+
+    vkvg_surface_destroy    (surf);
+    vkvg_device_destroy     (device);
+
+    vkengine_destroy (e);
+
+}
diff --git a/tests/common/test.h b/tests/common/test.h
new file mode 100644 (file)
index 0000000..dc83464
--- /dev/null
@@ -0,0 +1,77 @@
+#include "vkengine.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "time.h"
+#include "vkvg.h"
+
+#include "vkh_device.h"
+#include "vkh_presenter.h"
+
+#ifdef _WIN32 // MSC_VER
+    #define WIN32_LEAN_AND_MEAN
+    #define NOMINMAX
+    #include <Windows.h> // Windows.h -> WinDef.h defines min() max()
+
+    /*
+        typedef uint16_t WORD ;
+        typedef uint32_t DWORD;
+
+        typedef struct _FILETIME {
+            DWORD dwLowDateTime;
+            DWORD dwHighDateTime;
+        } FILETIME;
+
+        typedef struct _SYSTEMTIME {
+              WORD wYear;
+              WORD wMonth;
+              WORD wDayOfWeek;
+              WORD wDay;
+              WORD wHour;
+              WORD wMinute;
+              WORD wSecond;
+              WORD wMilliseconds;
+        } SYSTEMTIME, *PSYSTEMTIME;
+    */
+
+    // *sigh* Microsoft has this in winsock2.h because they are too lazy to put it in the standard location ... !?!?
+    typedef struct timeval {
+        long tv_sec;
+        long tv_usec;
+    } timeval;
+
+    // *sigh* no gettimeofday on Win32/Win64
+    int gettimeofday(struct timeval * tp, struct timezone * tzp)
+    {
+        // FILETIME Jan 1 1970 00:00:00
+        // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
+        static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL);
+
+        SYSTEMTIME  nSystemTime;
+        FILETIME    nFileTime;
+        uint64_t    nTime;
+
+        GetSystemTime( &nSystemTime );
+        SystemTimeToFileTime( &nSystemTime, &nFileTime );
+        nTime =  ((uint64_t)nFileTime.dwLowDateTime )      ;
+        nTime += ((uint64_t)nFileTime.dwHighDateTime) << 32;
+
+        tp->tv_sec  = (long) ((nTime - EPOCH) / 10000000L);
+        tp->tv_usec = (long) (nSystemTime.wMilliseconds * 1000);
+        return 0;
+    }
+#else
+    #include <sys/time.h>
+#endif // _WIN32
+
+extern float panX;
+extern float panY;
+extern float lastX;
+extern float lastY;
+extern float zoom;
+extern bool mouseDown;
+
+extern VkvgDevice device;
+extern VkvgSurface surf;
+
+void perform_test (void(*testfunc)(void),uint width, uint height);
+void randomize_color (VkvgContext ctx);
diff --git a/tests/common/vkengine.c b/tests/common/vkengine.c
new file mode 100644 (file)
index 0000000..6d0f0ef
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2018 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so, subject
+ * to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vkh.h"
+#include "vkengine.h"
+#include "vkh_app.h"
+#include "vkh_phyinfo.h"
+#include "vkh_presenter.h"
+#include "vkh_image.h"
+#include "vkh_device.h"
+
+bool vkeCheckPhyPropBlitSource (VkEngine e) {
+    VkFormatProperties formatProps;
+    vkGetPhysicalDeviceFormatProperties(e->dev->phy, e->renderer->format, &formatProps);
+
+#if VKVG_TILING_OPTIMAL
+    assert((formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) && "Format cannot be used as transfer source");
+#else
+    assert((formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) && "Format cannot be used as transfer source");
+#endif
+}
+
+VkSampleCountFlagBits getMaxUsableSampleCount(VkSampleCountFlags counts)
+{
+    if (counts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; }
+    if (counts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; }
+    if (counts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; }
+    if (counts & VK_SAMPLE_COUNT_8_BIT) { return VK_SAMPLE_COUNT_8_BIT; }
+    if (counts & VK_SAMPLE_COUNT_4_BIT) { return VK_SAMPLE_COUNT_4_BIT; }
+    if (counts & VK_SAMPLE_COUNT_2_BIT) { return VK_SAMPLE_COUNT_2_BIT; }
+    return VK_SAMPLE_COUNT_1_BIT;
+}
+
+void vkengine_dump_Infos (VkEngine e){
+    printf("max samples = %d\n", getMaxUsableSampleCount(e->gpu_props.limits.framebufferColorSampleCounts));
+    printf("max tex2d size = %d\n", e->gpu_props.limits.maxImageDimension2D);
+    printf("max tex array layers = %d\n", e->gpu_props.limits.maxImageArrayLayers);
+    printf("max mem alloc count = %d\n", e->gpu_props.limits.maxMemoryAllocationCount);
+
+    for (int i = 0; i < e->memory_properties.memoryHeapCount; i++) {
+        printf("Mem Heap %d\n", i);
+        printf("\tflags= %d\n", e->memory_properties.memoryHeaps[i].flags);
+        printf("\tsize = %d Mo\n", e->memory_properties.memoryHeaps[i].size/ (1024*1024));
+    }
+    for (int i = 0; i < e->memory_properties.memoryTypeCount; i++) {
+        printf("Mem type %d\n", i);
+        printf("\theap %d: ", e->memory_properties.memoryTypes[i].heapIndex);
+        if (e->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
+            printf("VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT|");
+        if (e->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
+            printf("VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT|");
+        if (e->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
+            printf("VK_MEMORY_PROPERTY_HOST_COHERENT_BIT|");
+        if (e->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
+            printf("VK_MEMORY_PROPERTY_HOST_CACHED_BIT|");
+        if (e->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT)
+            printf("VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT|");
+        printf("\n");
+    }
+}
+vk_engine_t* vkengine_create (VkPhysicalDeviceType preferedGPU, VkPresentModeKHR presentMode, uint32_t width, uint32_t height) {
+    vk_engine_t* e = (vk_engine_t*)calloc(1,sizeof(vk_engine_t));
+
+    glfwInit();
+    assert (glfwVulkanSupported()==GLFW_TRUE);
+
+    uint32_t enabledExtsCount = 0, phyCount = 0;
+    const char ** enabledExts = glfwGetRequiredInstanceExtensions (&enabledExtsCount);
+
+    e->app = vkh_app_create("vkvgTest", enabledExtsCount, enabledExts);
+
+    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+    glfwWindowHint(GLFW_RESIZABLE,  GLFW_TRUE);
+    glfwWindowHint(GLFW_FLOATING,   GLFW_FALSE);
+    glfwWindowHint(GLFW_DECORATED,  GLFW_TRUE);
+
+    e->window = glfwCreateWindow ((int)width, (int)height, "Window Title", NULL, NULL);
+
+    VkSurfaceKHR surf;
+    VkResult res = glfwCreateWindowSurface(e->app->inst, e->window, NULL, &surf);
+
+    VkhPhyInfo* phys = vkh_app_get_phyinfos (e->app, &phyCount, surf);
+
+    VkhPhyInfo pi = NULL;
+    for (int i=0; i<phyCount; i++){
+        pi = phys[i];
+        if (pi->properties.deviceType == preferedGPU)
+            break;
+    }
+
+    e->memory_properties = pi->memProps;
+    e->gpu_props = pi->properties;
+
+    uint32_t qCount = 0;
+    VkDeviceQueueCreateInfo pQueueInfos[3];
+    float queue_priorities[] = {0.0};
+
+    VkDeviceQueueCreateInfo qiG = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+                                   .queueCount = 1,
+                                   .queueFamilyIndex = pi->gQueue,
+                                   .pQueuePriorities = queue_priorities };
+    VkDeviceQueueCreateInfo qiC = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+                                   .queueCount = 1,
+                                   .queueFamilyIndex = pi->cQueue,
+                                   .pQueuePriorities = queue_priorities };
+    VkDeviceQueueCreateInfo qiT = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+                                   .queueCount = 1,
+                                   .queueFamilyIndex = pi->tQueue,
+                                   .pQueuePriorities = queue_priorities };
+
+    if (pi->gQueue == pi->cQueue){
+        if(pi->gQueue == pi->tQueue){
+            qCount=1;
+            pQueueInfos[0] = qiG;
+        }else{
+            qCount=2;
+            pQueueInfos[0] = qiG;
+            pQueueInfos[1] = qiT;
+        }
+    }else{
+        if((pi->gQueue == pi->tQueue) || (pi->cQueue==pi->tQueue)){
+            qCount=2;
+            pQueueInfos[0] = qiG;
+            pQueueInfos[1] = qiC;
+        }else{
+            qCount=3;
+            pQueueInfos[0] = qiG;
+            pQueueInfos[1] = qiC;
+            pQueueInfos[2] = qiT;
+        }
+    }
+
+    char const * dex [] = {"VK_KHR_swapchain"};
+
+    VkPhysicalDeviceFeatures enabledFeatures = {
+        .fillModeNonSolid = true,
+        //.sampleRateShading = true
+    };
+
+    VkDeviceCreateInfo device_info = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+                                       .queueCreateInfoCount = qCount,
+                                       .pQueueCreateInfos = (VkDeviceQueueCreateInfo*)&pQueueInfos,
+                                       .enabledExtensionCount = 1,
+                                       .ppEnabledExtensionNames = dex,
+                                       .pEnabledFeatures = &enabledFeatures
+                                     };
+
+    VkDevice dev;
+    VK_CHECK_RESULT(vkCreateDevice (pi->phy, &device_info, NULL, &dev));
+    e->dev = vkh_device_create(pi->phy, dev);
+    e->dev->instance = vkh_app_get_inst (e->app);
+
+    e->renderer = vkh_presenter_create
+            (e->dev, (uint32_t) pi->pQueue, surf, width, height, VK_FORMAT_B8G8R8A8_UNORM, presentMode);
+
+    vkh_app_free_phyinfos (phyCount, phys);
+
+    vkeCheckPhyPropBlitSource (e);
+
+    return e;
+}
+
+void vkengine_destroy (VkEngine e) {
+    vkDeviceWaitIdle(e->dev->dev);
+
+    VkSurfaceKHR surf = e->renderer->surface;
+
+    vkh_presenter_destroy (e->renderer);
+    vkDestroySurfaceKHR (e->app->inst, surf, NULL);
+
+    vkh_device_destroy (e->dev);
+
+    glfwDestroyWindow (e->window);
+    glfwTerminate ();
+
+    vkh_app_destroy (e->app);
+
+    free(e);
+}
+void vkengine_close (VkEngine e) {
+    glfwSetWindowShouldClose(e->window, GLFW_TRUE);
+}
+void vkengine_blitter_run (VkEngine e, VkImage img, uint32_t width, uint32_t height) {
+    VkhPresenter p = e->renderer;
+    vkh_presenter_build_blit_cmd (p, img, width, height);
+
+    while (!vkengine_should_close (e)) {
+        glfwPollEvents();
+        if (!vkh_presenter_draw (p))
+            vkh_presenter_build_blit_cmd (p, img, width, height);
+    }
+}
+inline bool vkengine_should_close (VkEngine e) {
+    return glfwWindowShouldClose (e->window);
+}
+
+VkDevice vkengine_get_device (VkEngine e){
+    return e->dev->dev;
+}
+VkPhysicalDevice vkengine_get_physical_device (VkEngine e){
+    return e->dev->phy;
+}
+VkQueue vkengine_get_queue (VkEngine e){
+    return e->renderer->queue;
+}
+uint32_t vkengine_get_queue_fam_idx (VkEngine e){
+    return e->renderer->qFam;
+}
+
+void vkengine_set_key_callback (VkEngine e, GLFWkeyfun key_callback){
+    glfwSetKeyCallback (e->window, key_callback);
+}
+void vkengine_set_mouse_but_callback (VkEngine e, GLFWmousebuttonfun onMouseBut){
+    glfwSetMouseButtonCallback(e->window, onMouseBut);
+}
+void vkengine_set_cursor_pos_callback (VkEngine e, GLFWcursorposfun onMouseMove){
+    glfwSetCursorPosCallback(e->window, onMouseMove);
+}
+void vkengine_set_scroll_callback (VkEngine e, GLFWscrollfun onScroll){
+    glfwSetScrollCallback(e->window, onScroll);
+}
+void vkengine_set_char_callback (VkEngine e, GLFWcharfun onChar){
+    glfwSetCharCallback(e->window, onChar);
+}
+
diff --git a/tests/common/vkengine.h b/tests/common/vkengine.h
new file mode 100644 (file)
index 0000000..b4505f8
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so, subject
+ * to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef VKENGINE_H
+#define VKENGINE_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#include <GLFW/glfw3.h>
+#include <vulkan/vulkan.h>
+
+#include "vkh.h"
+
+/* Number of samples needs to be the same at image creation,      */
+/* renderpass creation and pipeline creation.                     */
+#define FENCE_TIMEOUT 100000000
+
+typedef struct _vk_engine_t* VkEngine;
+
+typedef struct _vk_engine_t {
+    VkhApp              app;
+    VkPhysicalDeviceMemoryProperties    memory_properties;
+    VkPhysicalDeviceProperties          gpu_props;
+    VkhDevice           dev;
+    GLFWwindow*         window;
+    VkhPresenter        renderer;
+}vk_engine_t;
+
+vk_engine_t*   vkengine_create  (VkPhysicalDeviceType preferedGPU, VkPresentModeKHR presentMode, uint32_t width, uint32_t height);
+
+void vkengine_destroy       (VkEngine e);
+bool vkengine_should_close  (VkEngine e);
+void vkengine_close         (VkEngine e);
+void vkengine_dump_Infos    (VkEngine e);
+VkDevice            vkengine_get_device         (VkEngine e);
+VkPhysicalDevice    vkengine_get_physical_device(VkEngine e);
+VkQueue             vkengine_get_queue          (VkEngine e);
+uint32_t            vkengine_get_queue_fam_idx  (VkEngine e);
+
+void vkengine_get_queues_properties (vk_engine_t* e, VkQueueFamilyProperties** qFamProps, uint32_t* count);
+
+void vkengine_set_key_callback          (VkEngine e, GLFWkeyfun key_callback);
+void vkengine_set_mouse_but_callback    (VkEngine e, GLFWmousebuttonfun onMouseBut);
+void vkengine_set_cursor_pos_callback   (VkEngine e, GLFWcursorposfun onMouseMove);
+void vkengine_set_scroll_callback       (VkEngine e, GLFWscrollfun onScroll);
+void vkengine_set_char_callback         (VkEngine e, GLFWcharfun onChar);
+#endif
diff --git a/tests/curve.c b/tests/curve.c
new file mode 100644 (file)
index 0000000..11e44d4
--- /dev/null
@@ -0,0 +1,38 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    vkvg_set_line_width(ctx, 10);
+    vkvg_set_source_rgb   (ctx, 0,0,0);
+
+    vkvg_arc(ctx, 150, 100, 10, 0, M_PI*2);
+    vkvg_fill(ctx);
+    vkvg_arc(ctx, 200, 200, 10, 0, M_PI*2);
+    vkvg_fill(ctx);
+
+    vkvg_set_source_rgba   (ctx, 0.5,0.0,1.0,0.5);
+    vkvg_move_to(ctx,100,100);
+    vkvg_line_to(ctx,200,100);
+    vkvg_curve_to(ctx,250,100,300,150,300,200);
+    vkvg_line_to(ctx,300,300);
+    vkvg_curve_to(ctx,300,350,250,400,200,400);
+    vkvg_line_to(ctx,100,400);
+    vkvg_curve_to(ctx,50,400,10,350,10,300);
+    vkvg_line_to(ctx,10,200);
+    vkvg_curve_to(ctx,10,150,50,100,100,100);
+    vkvg_close_path(ctx);
+    //vkvg_curve_to(ctx, 150,100,200,150,200,200);
+    vkvg_fill_preserve(ctx);
+    vkvg_set_source_rgba   (ctx, 1,1,1.0,0.5);
+    vkvg_stroke(ctx);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/curve2.c b/tests/curve2.c
new file mode 100644 (file)
index 0000000..683c29b
--- /dev/null
@@ -0,0 +1,27 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    vkvg_move_to    (ctx, 100, 400);
+    vkvg_curve_to   (ctx, 100, 100, 600,700,600,400);
+    vkvg_curve_to   (ctx, 1000, 100, 100, 800, 1000, 800);
+    vkvg_curve_to   (ctx, 1000, 500, 700, 500, 700, 100);
+    vkvg_close_path(ctx);
+
+    //vkvg_set_source_rgba   (ctx, 0.5,0.0,1.0,0.5);
+    //vkvg_fill_preserve(ctx);
+
+    vkvg_set_source_rgba   (ctx, 1,0,0,1);
+    vkvg_set_line_width(ctx, 40);
+    vkvg_stroke(ctx);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/data/miroir.jpg b/tests/data/miroir.jpg
new file mode 100755 (executable)
index 0000000..03240fa
Binary files /dev/null and b/tests/data/miroir.jpg differ
diff --git a/tests/data/tiger.svg b/tests/data/tiger.svg
new file mode 100644 (file)
index 0000000..67ba636
--- /dev/null
@@ -0,0 +1,725 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg id="svg2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 900" version="1.1">
+ <g id="g4" fill="none" transform="matrix(1.7656463,0,0,1.7656463,324.90716,255.00942)">
+  <g id="g6" stroke-width="0.17200001" stroke="#000" fill="#FFF">
+   <path id="path8" d="m-122.3,84.285s0.1,1.894-0.73,1.875c-0.82-0.019-17.27-48.094-37.8-45.851,0,0,17.78-7.353,38.53,43.976z"/>
+  </g>
+  <g id="g10" stroke-width="0.17200001" stroke="#000" fill="#FFF">
+   <path id="path12" d="m-118.77,81.262s-0.55,1.816-1.32,1.517c-0.77-0.298,0.11-51.104-19.95-55.978,0,0,19.22-0.864,21.27,54.461z"/>
+  </g>
+  <g id="g14" stroke-width="0.17200001" stroke="#000" fill="#FFF">
+   <path id="path16" d="m-91.284,123.59s1.636,0.96,1.166,1.64c-0.471,0.67-49.642-12.13-59.102,6.23,0,0,3.68-18.89,57.936-7.87z"/>
+  </g>
+  <g id="g18" stroke-width="0.17200001" stroke="#000" fill="#FFF">
+   <path id="path20" d="m-94.093,133.8s1.856,0.4,1.622,1.19c-0.233,0.79-50.939,4.13-54.129,24.53,0,0-2.46-19.08,52.507-25.72z"/>
+  </g>
+  <g id="g22" stroke-width="0.17200001" stroke="#000" fill="#FFF">
+   <path id="path24" d="m-98.304,128.28s1.778,0.66,1.432,1.41-50.998-3.34-57.128,16.37c0,0,0.35-19.24,55.696-17.78z"/>
+  </g>
+  <g id="g26" stroke-width="0.17200001" stroke="#000" fill="#FFF">
+   <path id="path28" d="m-109.01,110.07s1.31,1.38,0.67,1.9-44.38-25.336-58.53-10.29c0,0,8.74-17.147,57.86,8.39z"/>
+  </g>
+  <g id="g30" stroke-width="0.17200001" stroke="#000" fill="#FFF">
+   <path id="path32" d="m-116.55,114.26s1.45,1.22,0.88,1.81c-0.58,0.59-46.97-20.148-59.32-3.6,0,0,6.74-18.023,58.44,1.79z"/>
+  </g>
+  <g id="g34" stroke-width="0.17200001" stroke="#000" fill="#FFF">
+   <path id="path36" d="m-119.15,118.34s1.6,1,1.11,1.67c-0.49,0.66-49.27-13.56-59.25,4.51,0,0,4.22-18.77,58.14-6.18z"/>
+  </g>
+  <g id="g38" stroke-width="0.17200001" stroke="#000" fill="#FFF">
+   <path id="path40" d="m-108.42,118.95s1.12,1.53,0.42,1.97c-0.7,0.43-40.77-30.818-56.73-17.71,0,0,10.87-15.884,56.31,15.74z"/>
+  </g>
+  <g id="g42" stroke-width="0.17200001" stroke="#000" fill="#FFF">
+   <path id="path44" d="m-128.2,90s0.6,1.8-0.2,2-29.4-41.8-48.6-34.2c0,0,15.2-11.8,48.8,32.2z"/>
+  </g>
+  <g id="g46" stroke-width="0.17200001" stroke="#000" fill="#FFF">
+   <path id="path48" d="m-127.5,96.979s0.97,1.629,0.23,1.996c-0.74,0.368-37.72-34.476-54.83-22.914,0,0,12.3-14.8,54.6,20.918z"/>
+  </g>
+  <g id="g50" stroke-width="0.17200001" stroke="#000" fill="#FFF">
+   <path id="path52" d="m-127.62,101.35s1.12,1.53,0.42,1.97c-0.7,0.43-40.77-30.818-56.73-17.713,0,0,10.87-15.881,56.31,15.743z"/>
+  </g>
+  <g id="g54" stroke="#000" fill="#FFF">
+   <path id="path56" d="m-129.83,103.06c0.5,6.05,1.49,12.62,3.23,15.74,0,0-3.6,12.4,5.2,25.6,0,0-0.4,7.2,1.2,10.4,0,0,4,8.4,8.8,9.2,3.88,0.65,12.607,3.72,22.468,5.12,0,0,17.132,14.08,13.932,26.88,0,0-0.4,16.4-4,18,0,0,11.6-11.2,2,5.6l-4.4,18.8s25.6-21.6,10-3.2l-10,26s19.6-18.4,12.4-10l-3.2,8.8s43.2-27.2,12.4,2.4c0,0,8-3.6,12.4-0.8,0,0,6.8-1.2,6,0.4,0,0-20.8,10.4-24.4,28.8,0,0,8.4-10,5.2,0.8l0.4,11.6s4-21.6,3.6,16c0,0,19.2-18,7.6,2.8v16.8s15.2-16.4,8.8-3.6c0,0,10-8.8,6,6.4,0,0-0.8,10.4,3.6-0.8,0,0,16-30.6,10-4.4,0,0-0.8,19.2,4,4.4,0,0,0.4,10.4,9.6,17.6,0,0-1.2-50.8,11.6-14.8l4,16.4s2.8-9.2,2.4-14.4l8,8s15.2-22.8,12-9.6c0,0-7.6,16-6,20.8,0,0,16.8-34.8,18-36.4,0,0-2,42.4,8.8,6.4,0,0,5.6,12,2.8,16.4,0,0,8-8,7.2-11.2,0,0,4.6-8.2,7.4,5.4,0,0,1.8,9.4,3.4,6.2,0,0,4,24,5.2,1.2,0,0,1.6-13.6-5.6-25.2,0,0,0.8-3.2-2-7.2,0,0,13.6,21.6,6.4-7.2,0,0,11.201,8,12.401,8,0,0-13.601-23.2-4.801-18.4,0,0-5.2-10.4,12.801,1.6,0,0-16.001-16,1.6-6.4,0,0,7.999,6.4,0.4-3.6,0,0-14.401-16,7.599,2,0,0,11.6,16.4,12.4,19.2,0,0-10-29.2-14.4-32,0,0,8.4-36.4,49.6-20.8,0,0,6.8,17.2,11.2-1.2,0,0,12.8-6.4,24,21.2,0,0,4-13.6,3.2-16.4,0,0,6.8,1.2,6,0,0,0,13.2,4.4,14.4,3.6,0,0,6.8,6.8,7.2,3.2,0,0,9.2,2.8,7.2-0.8,0,0,8.8,15.6,9.2,19.2l2.4-14,2,2.8s1.6-7.6,0.8-8.8,20,6.8,24.8,27.6l2,8.4s6-14.8,4.4-18.8c0,0,5.2,0.8,5.6,5.2,0,0,4-23.2-0.8-29.2,0,0,4.4-0.8,5.6,2.8v-7.2s7.2,0.8,7.2-1.6c0,0,4.4-4,6.4,0.8,0,0-12.4-35.2,6-16,0,0,7.2,10.8,3.6-8s-7.6-20.4-2.8-20.8c0,0,0.8-3.6-1.2-5.2s1.2,0,1.2,0,4.8,4-0.4-18c0,0,6.4,1.6-5.6-27.6,0,0,2.8-2.4-1.2-10.8,0,0,8,4.4,10.8,2.8,0,0-0.4-1.6-3.6-5.6,0,0-21.6-54.8-1.2-32.8,0,0,11.85,13.55,5.45-9.25,0,0-9.11-24.009-8.33-28.305l-429.55,23.015z"/>
+  </g>
+  <g id="g58" stroke="#000" fill="#cc7226">
+   <path id="path60" d="m299.72,80.245c0.62,0.181,2.83,1.305,4.08,2.955,0,0,6.8,10.8,1.6-7.6,0,0-9.2-28.8-0.4-17.6,0,0,6,7.2,2.8-6.4-3.86-16.427-6.4-22.8-6.4-22.8s11.6,4.8-15.2-34.8l8.8,3.6s-19.6-39.6-41.2-44.8l-8-6s38.4-38,25.6-74.8c0,0-6.8-5.2-16.4,4,0,0-6.4,4.8-12.4,3.2,0,0-30.8,1.2-32.8,1.2s-36.8-37.2-102.4-19.6c0,0-5.2,2-9.599,0.8,0,0-18.401-16-67.201,6.8,0,0-10,2-11.6,2s-4.4,0-12.4,6.4-8.4,7.2-10.4,8.8c0,0-16.4,11.2-21.2,12,0,0-11.6,6.4-16,16.4l-3.6,1.2s-1.6,7.2-2,8.4c0,0-4.8,3.6-5.6,9.2,0,0-8.8,6-8.4,10.4,0,0-1.6,5.2-2.4,10,0,0-7.2,4.8-6.4,7.6,0,0-7.6,14-6.4,20.8,0,0-6.4-0.4-9.2,2,0,0-0.8,4.8-2.4,5.2,0,0-2.8,1.2-0.4,5.2,0,0-1.6,2.8-2,4.4,0,0,0.8,2.8-3.6,8.4,0,0-6.4,18.8-4.4,24,0,0,0.4,4.8-2.4,6.4,0,0-3.6-0.4,4.8,11.6,0,0,0.8,1.2-2.4,3.6,0,0-17.2,3.6-19.6,20,0,0-13.6,14.8-13.6,20,0,2.305,0.27,5.452,0.97,10.06,0,0-0.57,8.34,27.03,9.14s402.72-31.355,402.72-31.355z"/>
+  </g>
+  <g id="g62" fill="#cc7226">
+   <path id="path64" d="m-115.6,102.6c-25-39.4-10.6,17-10.6,17,8.8,34.4,138.4-3.2,138.4-3.2s168.8-30.4,180-34.4,106.4,2.4,106.4,2.4l-5.6-16.8c-64.8-46.4-84-23.2-97.6-27.2s-11.2,5.6-14.4,6.4-42.4-24-48.8-23.2-31.74-22.951-16.8,8.8c16,34-58.4,39.2-75.2,28s7.2,18.4,7.2,18.4c18.4,20-16,3.2-16,3.2-34.4-12.8-58.4,12.8-61.6,13.6s-8,4-8.8-2.4-8.31-23.101-40,3.2c-20,16.6-33.8-5.4-33.8-5.4l-2.8,11.6z"/>
+  </g>
+  <g id="g66" fill="#e87f3a">
+   <path id="path68" d="m133.51,25.346c-6.4,0.8-31.77-22.939-16.8,8.8,16.6,35.2-58.4,39.2-75.2,28-16.801-11.2,7.2,18.4,7.2,18.4,18.4,20.004-16.001,3.2-16.001,3.2-34.4-12.8-58.4,12.8-61.6,13.6s-8,4.004-8.8-2.4c-0.8-6.4-8.179-22.934-40,3.2-21.236,17.344-34.729-4.109-34.729-4.109l-3.2,10.113c-25-39.804-9.93,18.51-9.93,18.51,8.81,34.4,139.06-4.51,139.06-4.51s168.8-30.404,180-34.404,105.53,2.327,105.53,2.327l-5.53-17.309c-64.8-46.4-83.2-22.618-96.8-26.618s-11.2,5.6-14.4,6.4-42.4-24-48.8-23.2z"/>
+  </g>
+  <g id="g70" fill="#ea8c4d">
+   <path id="path72" d="m134.82,27.091c-6.4,0.8-31.14-23.229-16.8,8.8,16.2,36.201-58.401,39.201-75.201,28.001s7.2,18.4,7.2,18.4c18.4,19.998-16,3.2-16,3.2-34.4-12.8-58.401,12.8-61.601,13.6s-8,3.998-8.8-2.4c-0.8-6.4-8.048-22.767-40,3.2-22.473,18.088-35.658-2.818-35.658-2.818l-3.6,8.616c-23.8-38.998-9.25,20.02-9.25,20.02,8.8,34.4,139.71-5.82,139.71-5.82s168.8-30.398,180-34.398,104.65,2.254,104.65,2.254l-5.45-17.818c-64.8-46.4-82.4-22.037-96-26.037s-11.2,5.6-14.4,6.401c-3.2,0.8-42.4-24.001-48.8-23.201z"/>
+  </g>
+  <g id="g74" fill="#ec9961">
+   <path id="path76" d="m136.13,28.837c-6.4,0.8-31.13-23.232-16.8,8.8,16.8,37.556-58.936,38.845-75.202,28-16.8-11.2,7.2,18.4,7.2,18.4,18.4,20.003-16,3.2-16,3.2-34.4-12.8-58.4,12.803-61.6,13.603s-8,4-8.8-2.403c-0.8-6.4-7.917-22.598-40.001,3.203-23.709,18.83-36.587-1.53-36.587-1.53l-4,7.13c-21.8-36.803-8.58,21.52-8.58,21.52,8.8,34.4,140.37-7.12,140.37-7.12s168.8-30.403,180-34.403,103.78,2.182,103.78,2.182l-5.38-18.327c-64.8-46.401-81.6-21.455-95.2-25.455s-11.2,5.6-14.4,6.4-42.4-24-48.8-23.2z"/>
+  </g>
+  <g id="g78" fill="#eea575">
+   <path id="path80" d="m137.44,30.583c-6.4,0.8-30.63-23.454-16.8,8.8,16.8,39.2-58.403,39.2-75.203,28s7.2,18.4,7.2,18.4c18.4,19.997-16,3.2-16,3.2-34.4-12.8-58.4,12.797-61.6,13.597s-8,4-8.8-2.4c-0.8-6.397-7.785-22.428-40,3.2-24.946,19.58-37.507-0.23-37.507-0.23l-4.4,5.63c-19.8-34.798-7.91,23.04-7.91,23.04,8.8,34.4,141.02-8.44,141.02-8.44s168.8-30.397,180-34.397,102.91,2.109,102.91,2.109l-5.31-18.837c-64.8-46.4-80.8-20.872-94.4-24.872s-11.2,5.6-14.4,6.4-42.4-24-48.8-23.2z"/>
+  </g>
+  <g id="g82" fill="#f1b288">
+   <path id="path84" d="m138.75,32.328c-6.4,0.8-32.37-22.651-16.8,8.8,19.2,38.8-58.404,39.2-75.204,28s7.2,18.4,7.2,18.4c18.4,20.002-16,3.2-16,3.2-34.4-12.8-58.4,12.802-61.6,13.602s-8,4-8.8-2.4c-0.8-6.402-7.654-22.265-40,3.2-26.182,20.33-38.436,1.05-38.436,1.05l-4.8,4.15c-18-33.202-7.24,24.54-7.24,24.54,8.8,34.4,141.68-9.74,141.68-9.74s168.8-30.402,180-34.402,102.03,2.036,102.03,2.036l-5.23-19.345c-64.8-46.4-80-20.291-93.6-24.291s-11.2,5.6-14.4,6.4-42.4-24-48.8-23.2z"/>
+  </g>
+  <g id="g86" fill="#f3bf9c">
+   <path id="path88" d="m140.06,34.073c-6.4,0.8-32.75-22.46-16.8,8.8,20.4,40.001-58.405,39.201-75.205,28.001s7.2,18.4,7.2,18.4c18.4,19.996-16,3.2-16,3.2-34.4-12.8-58.4,12.796-61.6,13.596s-8,4-8.8-2.4c-0.8-6.396-7.523-22.092-40,3.2-27.419,21.08-39.365,2.35-39.365,2.35l-5.2,2.65c-16-30.196-6.56,26.06-6.56,26.06,8.8,34.4,142.32-11.06,142.32-11.06s168.8-30.396,180-34.396,101.16,1.963,101.16,1.963l-5.16-19.854c-64.8-46.4-79.2-19.709-92.8-23.709-13.6-4.001-11.2,5.6-14.4,6.4s-42.4-24.001-48.8-23.201z"/>
+  </g>
+  <g id="g90" fill="#f5ccb0">
+   <path id="path92" d="m141.36,35.819c-6.4,0.8-33.84-21.875-16.8,8.8,22,39.6-58.396,39.2-75.196,28s7.2,18.4,7.2,18.4c18.4,20.001-16,3.2-16,3.2-34.4-12.8-58.4,12.801-61.6,13.601s-8,4-8.8-2.4c-0.8-6.401-7.391-21.928-40,3.2-28.655,21.82-40.294,3.64-40.294,3.64l-5.6,1.16c-14.4-28.401-5.89,27.56-5.89,27.56,8.8,34.4,142.98-12.36,142.98-12.36s168.8-30.401,180-34.401,100.3,1.891,100.3,1.891l-5.1-20.364c-64.8-46.4-78.4-19.127-92-23.127s-11.2,5.6-14.4,6.4-42.4-24-48.8-23.2z"/>
+  </g>
+  <g id="g94" fill="#f8d8c4">
+   <path id="path96" d="m142.67,37.565c-6.4,0.8-33.84-21.876-16.8,8.8,22,39.6-58.396,39.2-75.196,28s7.2,18.4,7.2,18.4c18.4,19.995-16,3.2-16,3.2-34.401-12.8-58.401,12.795-61.601,13.595s-8,4-8.8-2.4-7.259-21.755-40,3.2c-29.891,22.57-41.213,4.93-41.213,4.93l-6-0.33c-13.61-26.396-5.22,29.08-5.22,29.08,8.8,34.4,143.63-13.68,143.63-13.68s168.8-30.395,180-34.395,99.42,1.818,99.42,1.818l-5.01-20.873c-64.81-46.4-77.61-18.545-91.21-22.545s-11.2,5.6-14.4,6.4-42.4-24-48.8-23.2z"/>
+  </g>
+  <g id="g98" fill="#fae5d7">
+   <path id="path100" d="m143.98,39.31c-6.4,0.8-33.45-22.087-16.8,8.8,22,40.8-58.397,39.2-75.197,28s7.2,18.4,7.2,18.4c18.4,20-16,3.2-16,3.2-34.4-12.8-58.4,12.8-61.6,13.6-3.201,0.8-8.001,4-8.801-2.4s-7.128-21.592-40,3.2c-31.127,23.31-42.142,6.22-42.142,6.22l-6.4-1.82c-13-24-4.55,30.58-4.55,30.58,8.8,34.4,144.29-14.98,144.29-14.98s168.8-30.4,180-34.4,98.55,1.746,98.55,1.746l-4.95-21.382c-64.8-46.401-76.8-17.964-90.4-21.964s-11.2,5.6-14.4,6.4-42.4-24-48.8-23.2z"/>
+  </g>
+  <g id="g102" fill="#fcf2eb">
+   <path id="path104" d="m145.29,41.055c-6.4,0.8-32.37-22.644-16.8,8.8,21.2,42.801-58.398,39.201-75.198,28.001s7.2,18.4,7.2,18.4c18.4,20.004-16,3.2-16,3.2-34.4-12.8-58.4,12.804-61.6,13.604s-8,4-8.8-2.4-6.997-21.428-40,3.2c-32.365,24.05-43.072,7.5-43.072,7.5l-6.8-3.3c-12.8-23.204-3.87,32.09-3.87,32.09,8.8,34.4,144.94-16.29,144.94-16.29s168.8-30.4,180-34.404c11.2-4,97.67,1.674,97.67,1.674l-4.87-21.893c-64.8-46.4-76-17.381-89.6-21.381-13.6-4.001-11.2,5.6-14.4,6.4s-42.4-24.001-48.8-23.201z"/>
+  </g>
+  <g id="g106" fill="#FFF">
+   <path id="path108" d="m-115.8,119.6c-12.8-22-3.2,33.6-3.2,33.6,8.8,34.4,145.6-17.6,145.6-17.6s168.8-30.4,180-34.4,96.8,1.6,96.8,1.6l-4.8-22.4c-64.8-46.4-75.2-16.8-88.8-20.8s-11.2,5.6-14.4,6.4-42.4-24-48.8-23.2-31.62-23.007-16.8,8.8c22.23,47.707-60.759,37.627-75.2,28-16.8-11.2,7.2,18.4,7.2,18.4,18.4,20-16,3.2-16,3.2-34.4-12.8-58.4,12.8-61.6,13.6s-8,4-8.8-2.4-6.865-21.256-40,3.2c-33.6,24.8-44,8.8-44,8.8l-7.2-4.8z"/>
+  </g>
+  <g id="g110" fill="#000">
+   <path id="path112" d="m-74.2,149.6s-7.2,11.6,13.6,24.8c0,0,1.4,1.4-16.6-2.8,0,0-6.2-2-7.8-12.4,0,0-4.8-4.4-9.6-10s20.4,0.4,20.4,0.4z"/>
+  </g>
+  <g id="g114" fill="#CCC">
+   <path id="path116" d="m65.8,102s17.698,26.82,17.1,31.6c-1.3,10.4-1.5,20,1.7,24,3.201,4,12.001,37.2,12.001,37.2s-0.4,1.2,11.999-36.8c0,0,11.6-16-8.4-34.4,0,0-35.2-28.8-34.4-21.6z"/>
+  </g>
+  <g id="g118" fill="#000">
+   <path id="path120" d="m-54.2,176.4s11.2,7.2-3.2,38.4l6.4-2.4s-0.8,11.2-4,13.6l7.2-3.2s4.8,8,0.8,12.8c0,0,16.8,8,16,14.4,0,0,6.4-8,2.4-14.4s-11.2-2.4-10.4-20.8l-8.8,3.2s5.6-8.8,5.6-15.2l-8,2.4s15.469-26.58,4.8-28c-6-0.8-8.8-0.8-8.8-0.8z"/>
+  </g>
+  <g id="g122" fill="#CCC">
+   <path id="path124" d="m-21.8,193.2s2.8-4.4,0-3.6-34,15.6-40,25.2c0,0,34.4-24.4,40-21.6z"/>
+  </g>
+  <g id="g126" fill="#CCC">
+   <path id="path128" d="m-11.4,201.2s2.8-4.4,0-3.6-34,15.6-40,25.2c0,0,34.4-24.4,40-21.6z"/>
+  </g>
+  <g id="g130" fill="#CCC">
+   <path id="path132" d="m1.8,186s2.8-4.4,0-3.6-34,15.6-40,25.2c0,0,34.4-24.4,40-21.6z"/>
+  </g>
+  <g id="g134" fill="#CCC">
+   <path id="path136" d="m-21.4,229.6s0-6-2.8-5.2-38.8,18.4-44.8,28c0,0,42-25.6,47.6-22.8z"/>
+  </g>
+  <g id="g138" fill="#CCC">
+   <path id="path140" d="m-20.2,218.8s1.2-4.8-1.6-4c-2,0-28.4,11.6-34.4,21.2,0,0,29.6-21.6,36-17.2z"/>
+  </g>
+  <g id="g142" fill="#CCC">
+   <path id="path144" d="m-34.6,266.4-10,7.6s10.4-7.6,14-6.4c0,0-6.8,11.2-7.6,16.4,0,0,10.4-12.8,16-12.4,0,0,7.6,0.4,7.6,11.2,0,0,5.6-10.4,8.8-10,0,0,1.2,6.4,0,13.2,0,0,4-7.6,8-6,0,0,6.4-2,5.6,9.6,0,0,0,10.4-0.8,13.2,0,0,5.6-26.4,8-26.8,0,0,8-1.2,12.8,7.6,0,0-4-7.6,0.8-5.6,0,0,10.8,1.6,14,8.4,0,0-6.8-12-1.2-8.8l8,6.4s8.4,21.2,10.4,22.8c0,0-7.6-21.6-6-21.6,0,0-2-12,3.2,2.8,0,0-3.2-14,2.4-13.2s10,10.8,18.4,8.4c0,0,9.601,5.6,11.601-63.6l-124,46.8z"/>
+  </g>
+  <g id="g146" fill="#000">
+   <path id="path148" d="m-29.8,173.6s14.8-6,54.8,0c0,0,7.2,0.4,14-8.4s33.6-16,40-14l9.601,6.4,0.8,1.2s12.399,10.4,12.799,18-14.399,55.6-24,71.6c-9.6,16-19.2,28.4-38.4,26,0,0-20.8-4-46.4,0,0,0-29.2-1.6-32-9.6s11.2-23.2,11.2-23.2,4.4-8.4,3.2-22.8-0.8-42.4-5.6-45.2z"/>
+  </g>
+  <g id="g150" fill="#e5668c">
+   <path id="path152" d="M-7.8,175.6c8.4,18.4-21.2,83.6-21.2,83.6-2,1.6,12.66,7.65,22.8,5.2,10.946-2.64,51.2,1.6,51.2,1.6,23.6-15.6,36.4-60,36.4-60s10.401-24-7.2-27.2c-17.6-3.2-82-3.2-82-3.2z"/>
+  </g>
+  <g id="g154" fill="#b23259">
+   <path id="path156" d="m-9.831,206.5c3.326-12.79,4.91-24.59,2.031-30.9,0,0,62.4,6.4,73.6-14.4,4.241-7.87,19.001,22.8,18.6,32.4,0,0-63,14.4-77.8,3.2l-16.431,9.7z"/>
+  </g>
+  <g id="g158" fill="#a5264c">
+   <path id="path160" d="m-5.4,222.8s2,7.2-0.4,11.2c0,0-1.6,0.8-2.8,1.2,0,0,1.2,3.6,7.2,5.2,0,0,2,4.4,4.4,4.8s7.2,6,11.2,4.8,15.2-5.2,15.2-5.2,5.6-3.2,14.4,0.4c0,0,2.375-0.8,2.8-4.8,0.5-4.7,3.6-8.4,5.6-10.4s11.6-14.8,10.4-15.2-68,8-68,8z"/>
+  </g>
+  <g id="g162" stroke="#000" fill="#ff727f">
+   <path id="path164" d="m-9.8,174.4s-2.8,22.4,0.4,30.8,2.4,10.4,1.6,14.4,3.6,14,9.2,20l12,1.6s15.2-3.6,24.4-0.8c0,0,8.994,1.34,12.4-13.6,0,0,4.8-6.4,12-9.2s14.4-44.4,10.4-52.4-18.4-12.4-34.4,3.2-18-1.2-48,6z"/>
+  </g>
+  <g id="g166" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path168" d="m-8.2,249.2s-0.8-2-5.2-2.4c0,0-22.4-3.6-30.8-16,0,0-6.8-5.6-2.4,6,0,0,10.4,20.4,17.2,23.2,0,0,16.4,4,21.2-10.8z"/>
+  </g>
+  <g id="g170" fill="#cc3f4c">
+   <path id="path172" d="m71.742,185.23c0.659-7.91,2.612-16.52,0.858-20.03-6.446-12.89-23.419-7.5-34.4,3.2-16,15.6-18-1.2-48,6,0,0-1.745,13.96-0.905,23.98,0,0,37.305-11.58,38.105-5.98,0,0,1.6-3.2,10.8-3.2s31.942-1.17,33.542-3.97z"/>
+  </g>
+  <g id="g174" stroke-width="2" stroke="#a51926">
+   <path id="path176" d="m28.6,175.2s4.8,4.8,1.2,14.4c0,0-14.4,16-12.4,30"/>
+  </g>
+  <g id="g178" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path180" d="m-19.4,260s-4.4-12.8,4.4-6l3.6,3.6c-1.2,1.6-6.8,5.6-8,2.4z"/>
+  </g>
+  <g id="g182" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path184" d="m-14.36,261.2s-3.52-10.24,3.52-4.8l2.88,2.88c-4.56,1.28,0,3.84-6.4,1.92z"/>
+  </g>
+  <g id="g186" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path188" d="m-9.56,261.2s-3.52-10.24,3.52-4.8l2.88,2.88c-3.36,1.28,0,3.84-6.4,1.92z"/>
+  </g>
+  <g id="g190" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path192" d="m-2.96,261.4s-3.52-10.24,3.52-4.8c0,0,4.383,2.33,2.881,2.88-2.961,1.08,0,3.84-6.401,1.92z"/>
+  </g>
+  <g id="g194" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path196" d="m3.52,261.32s-3.52-10.24,3.521-4.8l2.88,2.88c-0.96,1.28,0,3.84-6.401,1.92z"/>
+  </g>
+  <g id="g198" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path200" d="m10.2,262s-4.8-12.4,4.4-6l3.6,3.6c-1.2,1.6,0,4.8-8,2.4z"/>
+  </g>
+  <g id="g202" stroke-width="2" stroke="#a5264c">
+   <path id="path204" d="m-18.2,244.8s13.2-2.8,19.2,0.4c0,0,6,1.2,7.2,0.8s4.4-0.8,4.4-0.8"/>
+  </g>
+  <g id="g206" stroke-width="2" stroke="#a5264c">
+   <path id="path208" d="m15.8,253.6s12-13.6,24-9.2c7.016,2.57,6-0.8,6.8-3.6s1-7,6-10"/>
+  </g>
+  <g id="g210" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path212" d="m33,237.6s-4-10.8-6.8,2-6,16.4-7.6,19.2c0,0,0,5.2,8.4,4.8,0,0,10.8-0.4,11.2-3.2s-1.2-14.4-5.2-22.8z"/>
+  </g>
+  <g id="g214" stroke-width="2" stroke="#a5264c">
+   <path id="path216" d="m47,244.8s3.6-2.4,6-1.2"/>
+  </g>
+  <g id="g218" stroke-width="2" stroke="#a5264c">
+   <path id="path220" d="m53.5,228.4s2.9-4.9,7.7-5.7"/>
+  </g>
+  <g id="g222" fill="#b2b2b2">
+   <path id="path224" d="m-25.8,265.2s18,3.2,22.4,1.6l0.4,2-20.8-1.2s-11.6-5.6-2-2.4z"/>
+  </g>
+  <g id="g226" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path228" d="m-11.8,172,19.6,0.8s7.2,30.8,3.6,38.4c0,0-1.2,2.8-4-2.8,0,0-18.4-32.8-21.6-34.8s1.2-1.6,2.4-1.6z"/>
+  </g>
+  <g id="g230" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path232" d="m-88.9,169.3s8.9,1.7,21.5,4.3c0,0,4.8,22.4,8,27.2s-0.4,4.8-4,2-18.4-16.8-20.4-21.2-5.1-12.3-5.1-12.3z"/>
+  </g>
+  <g id="g234" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path236" d="m-67.039,173.82s5.8,1.55,6.809,3.76c1.008,2.22-1.202,5.51-1.202,5.51s-1,3.31-2.202,1.15c-1.202-2.17-4.074-9.83-3.405-10.42z"/>
+  </g>
+  <g id="g238" fill="#000">
+   <path id="path240" d="m-67,173.6s3.6,5.2,7.2,5.2,3.982-0.41,6.8,0.2c4.6,1,4.2-1,10.8,0.2,2.64,0.48,5.2-0.4,8,0.8s6,0.4,7.2-1.6,6-6.2,6-6.2-12.8,1.8-15.6,2.6c0,0-22.4,1.2-30.4-1.2z"/>
+  </g>
+  <g id="g242" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path244" d="m-22.4,173.8s-6.45,3.5-6.85,5.9,5.25,6.1,5.25,6.1,2.75,4.6,3.35,2.2-0.95-13.8-1.75-14.2z"/>
+  </g>
+  <g id="g246" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path248" d="m-59.885,179.26s7.007,11.19,7.224-0.02c0,0,0.557-1.26-1.203-1.28-6.075-0.07-4.554-4.18-6.021,1.3z"/>
+  </g>
+  <g id="g250" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path252" d="m-52.707,179.51s7.921,11.19,7.285-0.09c0,0,0.007-0.33-1.746-0.48-4.747-0.42-4.402-4.94-5.539,0.57z"/>
+  </g>
+  <g id="g254" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path256" d="m-45.494,179.52s7.96,10.63,7.291,0.96c0,0,0.119-1.23-1.535-1.53-3.892-0.71-4.103-3.95-5.756,0.57z"/>
+  </g>
+  <g id="g258" stroke-width="0.5" stroke="#000" fill="#FFC">
+   <path id="path260" d="m-38.618,179.6s7.9,11.56,8.248,1.78c0,0,1.644-1.38-0.102-1.6-5.818-0.74-5.02-5.19-8.146-0.18z"/>
+  </g>
+  <g id="g262" fill="#e5e5b2">
+   <path id="path264" d="m-74.792,183.13-7.658-1.53c-2.6-5-4.7-11.15-4.7-11.15s6.35,1,18.85,3.8c0,0,0.876,3.32,2.348,9.11l-8.84-0.23z"/>
+  </g>
+  <g id="g266" fill="#e5e5b2">
+   <path id="path268" d="m-9.724,178.47c-1.666-2.51-2.983-4.26-3.633-4.67-3.013-1.88,1.13-1.51,2.259-1.51l18.454,0.76s0.524,2.24,1.208,5.63c0,0-10.088-2.01-18.288-0.21z"/>
+  </g>
+  <g id="g270" fill="#cc7226">
+   <path id="path272" d="m43.88,40.321c27.721,3.96,53.241-31.68,55.001-41.361,1.759-9.68-8.36-21.56-8.36-21.56,1.32-3.08-3.52-17.16-8.8-26.4s-21.181-8.266-38.721-9.24c-15.84-0.88-34.32,22.44-35.64,24.2s4.84,40.041,6.16,45.761-1.32,32.12-1.32,32.12c34.24-9.1,3.96-7.48,31.68-3.52z"/>
+  </g>
+  <g id="g274" fill="#ea8e51">
+   <path id="path276" d="m8.088-33.392c-1.296,1.728,4.752,39.313,6.048,44.929s-1.296,31.536-1.296,31.536c32.672-8.88,3.888-7.344,31.104-3.456,27.217,3.888,52.273-31.104,54.001-40.609,1.728-9.504-8.208-21.168-8.208-21.168,1.296-3.024-3.456-16.848-8.64-25.92s-20.795-8.115-38.017-9.072c-15.552-0.864-33.696,22.032-34.992,23.76z"/>
+  </g>
+  <g id="g278" fill="#efaa7c">
+   <path id="path280" d="m8.816-32.744c-1.272,1.696,4.664,38.585,5.936,44.097s-1.272,30.952-1.272,30.952c31.404-9.16,3.816-7.208,30.528-3.392,26.713,3.816,51.305-30.528,53.001-39.857,1.696-9.328-8.056-20.776-8.056-20.776,1.272-2.968-3.392-16.536-8.48-25.44s-20.41-7.965-37.313-8.904c-15.264-0.848-33.072,21.624-34.344,23.32z"/>
+  </g>
+  <g id="g282" fill="#f4c6a8">
+   <path id="path284" d="m9.544-32.096c-1.248,1.664,4.576,37.857,5.824,43.265s-1.248,30.368-1.248,30.368c29.436-9.04,3.744-7.072,29.952-3.328,26.209,3.744,50.337-29.952,52.001-39.104,1.664-9.153-7.904-20.385-7.904-20.385,1.248-2.912-3.328-16.224-8.32-24.96s-20.025-7.815-36.609-8.736c-14.976-0.832-32.448,21.216-33.696,22.88z"/>
+  </g>
+  <g id="g286" fill="#f9e2d3">
+   <path id="path288" d="m10.272-31.448c-1.224,1.632,4.488,37.129,5.712,42.433s-1.224,29.784-1.224,29.784c27.868-8.92,3.672-6.936,29.376-3.264,25.705,3.672,49.369-29.376,51.001-38.353,1.632-8.976-7.752-19.992-7.752-19.992,1.224-2.856-3.264-15.912-8.16-24.48s-19.64-7.665-35.905-8.568c-14.688-0.816-31.824,20.808-33.048,22.44z"/>
+  </g>
+  <g id="g290" fill="#FFF">
+   <path id="path292" d="M44.2,36.8c25.2,3.6,48.401-28.8,50.001-37.6s-7.6-19.6-7.6-19.6c1.2-2.8-3.201-15.6-8.001-24s-19.254-7.514-35.2-8.4c-14.4-0.8-31.2,20.4-32.4,22s4.4,36.4,5.6,41.6-1.2,29.2-1.2,29.2c25.5-8.6,3.6-6.8,28.8-3.2z"/>
+  </g>
+  <g id="g294" fill="#CCC">
+   <path id="path296" d="m90.601,2.8s-27.801,7.6-39.401,6c0,0-15.8-6.6-24.6,15.2,0,0-3.6,7.2-5.6,9.2s69.601-30.4,69.601-30.4z"/>
+  </g>
+  <g id="g298" fill="#000">
+   <path id="path300" d="m94.401,0.6s-29.001,12.2-39.001,11.8c0,0-16.4-4.6-24.8,10,0,0-8.4,9.2-11.6,10.8,0,0-0.4,1.6,6-2.4l10.4,5.2s14.8,9.6,24.4-6.4c0,0,4-11.2,4-13.2s21.2-7.6,22.801-8c1.6-0.4,8.2-4.6,7.8-7.8z"/>
+  </g>
+  <g id="g302" fill="#99cc32">
+   <path id="path304" d="m47,36.514c-6.872,0-15.245-3.865-15.245-10.114,0-6.248,8.373-12.513,15.245-12.513,6.874,0,12.446,5.065,12.446,11.313,0,6.249-5.572,11.314-12.446,11.314z"/>
+  </g>
+  <g id="g306" fill="#659900">
+   <path id="path308" d="m43.377,19.83c-4.846,0.722-9.935,2.225-9.863,2.009,1.54-4.619,7.901-7.952,13.486-7.952,4.296,0,8.084,1.978,10.32,4.988,0,0-5.316-0.33-13.943,0.955z"/>
+  </g>
+  <g id="g310" fill="#FFF">
+   <path id="path312" d="m55.4,19.6s-4.4-3.2-4.4-1c0,0,3.6,4.4,4.4,1z"/>
+  </g>
+  <g id="g314" fill="#000">
+   <path id="path316" d="m45.4,27.726c-2.499,0-4.525-2.026-4.525-4.526,0-2.499,2.026-4.525,4.525-4.525,2.5,0,4.526,2.026,4.526,4.525,0,2.5-2.026,4.526-4.526,4.526z"/>
+  </g>
+  <g id="g318" fill="#cc7226">
+   <path id="path320" d="m-58.6,14.4s-3.2-21.2-0.8-25.6c0,0,10.8-10,10.4-13.6,0,0-0.4-18-1.6-18.8s-8.8-6.8-14.8-0.4c0,0-10.4,18-9.6,24.4v2s-7.6-0.4-9.2,1.6c0,0-1.2,5.2-2.4,5.6,0,0-2.8,2.4-0.8,5.2,0,0-2,2.4-1.6,6.4l7.6,4s2,14.4,12.8,19.6c4.836,2.329,8-4.4,10-10.4z"/>
+  </g>
+  <g id="g322" fill="#FFF">
+   <path id="path324" d="m-59.6,12.56s-2.88-19.08-0.72-23.04c0,0,9.72-9,9.36-12.24,0,0-0.36-16.2-1.44-16.92s-7.92-6.12-13.32-0.36c0,0-9.36,16.2-8.64,21.96v1.8s-6.84-0.36-8.28,1.44c0,0-1.08,4.68-2.16,5.04,0,0-2.52,2.16-0.72,4.68,0,0-1.8,2.16-1.44,5.76l6.84,3.6s1.8,12.96,11.52,17.64c4.352,2.095,7.2-3.96,9-9.36z"/>
+  </g>
+  <g id="g326" fill="#eb955c">
+   <path id="path328" d="m-51.05-42.61c-1.09-0.86-8.58-6.63-14.43-0.39,0,0-10.14,17.55-9.36,23.79v1.95s-7.41-0.39-8.97,1.56c0,0-1.17,5.07-2.34,5.46,0,0-2.73,2.34-0.78,5.07,0,0-1.95,2.34-1.56,6.24l7.41,3.9s1.95,14.04,12.48,19.11c4.714,2.27,7.8-4.29,9.75-10.14,0,0-3.12-20.67-0.78-24.96,0,0,10.53-9.75,10.14-13.26,0,0-0.39-17.55-1.56-18.33z"/>
+  </g>
+  <g id="g330" fill="#f2b892">
+   <path id="path332" d="m-51.5-41.62c-0.98-0.92-8.36-6.46-14.06-0.38,0,0-9.88,17.1-9.12,23.18v1.9s-7.22-0.38-8.74,1.52c0,0-1.14,4.94-2.28,5.32,0,0-2.66,2.28-0.76,4.94,0,0-1.9,2.28-1.52,6.08l7.22,3.8s1.9,13.68,12.16,18.62c4.594,2.212,7.6-4.18,9.5-9.88,0,0-3.04-20.14-0.76-24.32,0,0,10.26-9.5,9.88-12.92,0,0-0.38-17.1-1.52-17.86z"/>
+  </g>
+  <g id="g334" fill="#f8dcc8">
+   <path id="path336" d="m-51.95-40.63c-0.87-0.98-8.14-6.29-13.69-0.37,0,0-9.62,16.65-8.88,22.57v1.85s-7.03-0.37-8.51,1.48c0,0-1.11,4.81-2.22,5.18,0,0-2.59,2.22-0.74,4.81,0,0-1.85,2.22-1.48,5.92l7.03,3.7s1.85,13.32,11.84,18.13c4.473,2.154,7.4-4.07,9.25-9.62,0,0-2.96-19.61-0.74-23.68,0,0,9.99-9.25,9.62-12.58,0,0-0.37-16.65-1.48-17.39z"/>
+  </g>
+  <g id="g338" fill="#FFF">
+   <path id="path340" d="m-59.6,12.46s-2.88-18.98-0.72-22.94c0,0,9.72-9,9.36-12.24,0,0-0.36-16.2-1.44-16.92-0.76-1.04-7.92-6.12-13.32-0.36,0,0-9.36,16.2-8.64,21.96v1.8s-6.84-0.36-8.28,1.44c0,0-1.08,4.68-2.16,5.04,0,0-2.52,2.16-0.72,4.68,0,0-1.8,2.16-1.44,5.76l6.84,3.6s1.8,12.96,11.52,17.64c4.352,2.095,7.2-4.06,9-9.46z"/>
+  </g>
+  <g id="g342" fill="#CCC">
+   <path id="path344" d="m-62.7,6.2s-21.6-10.2-22.5-11c0,0,9.1,8.2,9.9,8.2s12.6,2.8,12.6,2.8z"/>
+  </g>
+  <g id="g346" fill="#000">
+   <path id="path348" d="m-79.8,0s18.4,3.6,18.4,8c0,2.912-0.243,16.331-5.6,14.8-8.4-2.4-4.8-16.8-12.8-22.8z"/>
+  </g>
+  <g id="g350" fill="#99cc32">
+   <path id="path352" d="m-71.4,3.8s8.978,1.474,10,4.2c0.6,1.6,1.263,9.908-4.2,11-4.552,0.911-6.782-9.31-5.8-15.2z"/>
+  </g>
+  <g id="g354" fill="#000">
+   <path id="path356" d="m14.595,46.349c-0.497-1.742,0.814-1.611,2.605-2.149,2-0.6,14.2-4.4,15-7s14,1.8,14,1.8c1.8,0.8,6.2,3.4,6.2,3.4,4.8,1.2,11.4,1.6,11.4,1.6,2.4,1,5.8,3.8,5.8,3.8,14.6,10.2,27.001,3,27.001,3,19.999-6.6,13.999-23.8,13.999-23.8-3-9,0.2-12.4,0.2-12.4,0.2-3.8,7.4,2.6,7.4,2.6,2.6,4.2,3.4,9.2,3.4,9.2,8,11.2,4.6-6.6,4.6-6.6,0.2-1-2.6-4.6-2.6-5.8s-1.8-4.6-1.8-4.6c-3-3.4-0.6-10.4-0.6-10.4,1.8-13.8-0.4-12-0.4-12-1.2-1.8-10.4,8.2-10.4,8.2-2.2,3.4-8.2,5-8.2,5-2.799,1.8-6.199,0.4-6.199,0.4-2.6-0.4-8.2,6.6-8.2,6.6,2.8-0.2,5.2,4.2,7.6,4.4s4.2-2.4,5.799-3c1.6-0.6,4.4,5.2,4.4,5.2,0.4,2.6-5.2,7.4-5.2,7.4-0.4,4.6-1.999,3-1.999,3-3-0.6-4.2,3.2-5.2,7.8s-5.2,5-5.2,5c-1.6,7.4-2.801,4.4-2.801,4.4-0.2-5.6-6.2,0.2-6.2,0.2-1.2,2-5.8-0.2-5.8-0.2-6.8-2-4.4-4-4.4-4,1.8-2.2,13,0,13,0,2.2-1.6-5.8-5.6-5.8-5.6-0.6-1.8,0.4-6.2,0.4-6.2,1.2-3.2,8-8.8,8-8.8,9.401-1.2,6.601-2.8,6.601-2.8-6.2-5.2-12.001,2.4-12.001,2.4-2.2,6.2-19.6,21.2-19.6,21.2-4.8,3.4-2.2-3.4-6.2,0s-24.6-5.6-24.6-5.6c-11.562-1.193-14.294,14.549-17.823,11.429,0,0,5.418,8.52,3.818,2.92z"/>
+  </g>
+  <g id="g358" fill="#000">
+   <path id="path360" d="m209.4-120s-25.6,8-28.4,26.8c0,0-2.4,22.8,18,40.4,0,0,0.4,6.4,2.4,9.6,0,0-1.6,4.8,17.2-2.8l27.2-8.4s6.4-2.4,11.6-11.2,20.4-27.6,16.8-52.8c0,0,1.2-11.2-4.8-11.6,0,0-8.4-1.6-15.6,6,0,0-6.8,3.2-9.2,2.8l-35.2,1.2z"/>
+  </g>
+  <g id="g362" fill="#000">
+   <path id="path364" d="m264.02-120.99s2.1-8.93-2.74-4.09c0,0-7.04,5.72-14.52,5.72,0,0-14.52,2.2-18.92,15.4,0,0-3.96,26.84,3.96,32.56,0,0,4.84,7.48,11.88,0.88s22.54-36.83,20.34-50.47z"/>
+  </g>
+  <g id="g366" fill="#323232">
+   <path id="path368" d="m263.65-120.63s2.09-8.75-2.66-3.99c0,0-6.92,5.61-14.26,5.61,0,0-14.26,2.16-18.58,15.12,0,0-3.89,26.354,3.89,31.97,0,0,4.75,7.344,11.66,0.864,6.92-6.48,22.11-36.184,19.95-49.574z"/>
+  </g>
+  <g id="g370" fill="#666">
+   <path id="path372" d="m263.27-120.27s2.08-8.56-2.58-3.9c0,0-6.78,5.51-13.99,5.51,0,0-14,2.12-18.24,14.84,0,0-3.81,25.868,3.82,31.38,0,0,4.66,7.208,11.45,0.848,6.78-6.36,21.66-35.538,19.54-48.678z"/>
+  </g>
+  <g id="g374" fill="#999">
+   <path id="path376" d="m262.9-119.92s2.07-8.37-2.51-3.79c0,0-6.65,5.41-13.73,5.41,0,0-13.72,2.08-17.88,14.56,0,0-3.75,25.372,3.74,30.78,0,0,4.58,7.072,11.23,0.832,6.66-6.24,21.23-34.892,19.15-47.792z"/>
+  </g>
+  <g id="g378" fill="#CCC">
+   <path id="path380" d="m262.53-119.56s2.06-8.18-2.43-3.7c0,0-6.53,5.31-13.47,5.31,0,0-13.46,2.04-17.54,14.28,0,0-3.67,24.886,3.67,30.19,0,0,4.49,6.936,11.02,0.816,6.52-6.12,20.79-34.246,18.75-46.896z"/>
+  </g>
+  <g id="g382" fill="#FFF">
+   <path id="path384" d="m262.15-119.2s2.05-8-2.35-3.6c0,0-6.4,5.2-13.2,5.2,0,0-13.2,2-17.2,14,0,0-3.6,24.4,3.6,29.6,0,0,4.4,6.8,10.8,0.8s20.35-33.6,18.35-46z"/>
+  </g>
+  <g id="g386" fill="#992600">
+   <path id="path388" d="m50.6,84s-20.4-19.2-28.4-20c0,0-34.4-4-49.2,14,0,0,17.6-20.4,45.2-14.8,0,0-21.6-4.4-34-1.2l-26.4,14-2.8,4.8s4-14.8,22.4-20.8c0,0,22.8-4.8,33.6,0,0,0-21.6-6.8-31.6-4.8,0,0-30.4-2.4-43.2,24,0,0,4-14.4,18.8-21.6,0,0,13.6-8.8,34-6,0,0,14.4,3.2,19.6,5.6s4-0.4-4.4-5.2c0,0-5.6-10-19.6-9.6,0,0-42.8,3.6-53.2,15.6,0,0,13.6-11.2,24-14,0,0,22.4-8,30.8-7.2,0,0,24.8,1,32.4-3,0,0-11.2,5-8,8.2s10,10.8,10,12,24.2,23.3,27.8,27.7l2.2,2.3z"/>
+  </g>
+  <g id="g390" fill="#CCC">
+   <path id="path392" d="m189,278s-15.5-36.5-28-46c0,0,26,16,29.5,34,0,0,0,10-1.5,12z"/>
+  </g>
+  <g id="g394" fill="#CCC">
+   <path id="path396" d="m236,285.5s-26.5-55-45-79c0,0,43.5,37.5,48.5,64l0.5,5.5-3-2.5s-0.5,9-1,12z"/>
+  </g>
+  <g id="g398" fill="#CCC">
+   <path id="path400" d="m292.5,237s-62.5-59.5-64-62c0,0,60.5,66,63.5,73.5,0,0-2-9,0.5-11.5z"/>
+  </g>
+  <g id="g402" fill="#CCC">
+   <path id="path404" d="m104,280.5s19.5-52,38.5-29.5c0,0,15,10,14.5,13,0,0-4-6.5-22-6,0,0-19-3-31,22.5z"/>
+  </g>
+  <g id="g406" fill="#CCC">
+   <path id="path408" d="m294.5,153s-45-28.5-52.5-30c-11.81-2.36,49.5,29,54.5,39.5,0,0,2-2.5-2-9.5z"/>
+  </g>
+  <g id="g410" fill="#000">
+   <path id="path412" d="m143.8,259.6s20.4-2,27.2-8.8l4.4,3.6,17.6-38.4,3.6,5.2s14.4-14.8,13.6-22.8,12.8,6,12.8,6-0.8-11.6,6.4-4.8c0,0-2.4-15.6,6-7.6,0,0-10.54-30.16,12-4.4,5.6,6.4,1.2-0.4,1.2-0.4s-26-48-4.4-33.6c0,0,2-22.8,0.8-27.2s-3.2-26.8-8-32,0.4-6.8,6-1.6c0,0-11.2-24,2-12,0,0-3.6-15.2-8-18,0,0-5.6-17.2,9.6-6.4,0,0-4.4-12.4-7.6-15.6,0,0-11.6-27.6-4.4-22.8l4.4,3.6s-6.8-14-0.4-9.6,6.4,4,6.4,4-21.2-33.2-0.8-15.6c0,0-8.16-13.918-11.6-20.8,0,0-18.8-20.4-4.4-14l4.8,1.6s-8.8-10-16.8-11.6,2.4-8,8.8-6,22,9.6,22,9.6,12.8,18.8,16.8,19.2c0,0-20-7.6-14,0.4,0,0,14.4,14,7.2,13.6,0,0-6,7.2-1.2,16,0,0-18.46-18.391-3.6,7.2l6.8,16.4s-24.4-24.8-13.2-2.8c0,0,17.2,23.6,19.2,24s6.4,9.2,6.4,9.2l-4.4-2,5.2,8.8s-11.2-12-5.2,1.2l5.6,14.4s-20.4-22-6.8,7.6c0,0-16.4-5.2-7.6,12,0,0-1.6,16-1.2,21.2s1.6,33.6-2.8,41.6,6,27.2,8,31.2,5.6,14.8-3.2,5.6-4.4-3.6-2.4,5.2,8,24.4,7.2,30c0,0-1.2,1.2-4.4-2.4,0,0-14.8-22.8-13.2-8.4,0,0-1.2,8-4.4,16.8,0,0-3.2,10.8-3.2,2,0,0-3.2-16.8-6-9.2s-6.4,13.6-9.2,16-8-20.4-9.2-10c0,0-12-12.4-16.8,4l-11.6,16.4s-0.4-12.4-1.6-6.4c0,0-30,6-40.4,1.6z"/>
+  </g>
+  <g id="g414" fill="#000">
+   <path id="path416" d="m109.4-97.2s-11.599-8-15.599-7.6,27.599-8.8,68.799,18.8c0,0,4.8,2.8,8.4,2.4,0,0,3.2,2.4,0.4,6,0,0-8.8,9.6,2.4,20.8,0,0,18.4,6.8,12.8-2,0,0,10.8,4,13.2,8s1.2,0,1.2,0l-12.4-12.4s-5.2-2-8-10.4-5.2-18.4-0.8-21.6c0,0-4,4.4-3.2,0.4s4.4-7.6,6-8,18-16.2,24.8-16.6c0,0-9.2,1.4-12.2,0.4s-29.6-12.4-35.6-13.6c0,0-16.8-6.6-4.8-4.6,0,0,35.8,3.8,54,17,0,0-7.2-8.4-25.6-15.4,0,0-22.2-12.6-57.4-7.6,0,0-17.8,3.2-25.6,5,0,0-2.599-0.6-3.199-1s-12.401-9.4-40.001-2.4c0,0-17,4.6-25.6,9.4,0,0-15.2,1.2-18.8,4.4,0,0-18.6,14.6-20.6,15.4s-13.4,8.4-14.2,8.8c0,0,24.6-6.6,27-9s19.8-5,22.2-3.6,10.8,0.8,1.2,1.4c0,0,75.6,14.8,76.4,16.8s4.8,0.8,4.8,0.8z"/>
+  </g>
+  <g id="g418" fill="#cc7226">
+   <path id="path420" d="m180.8-106.4s-10.2-7.4-12.2-7.4-14.4-10.2-18.6-9.8-16.4-9.6-43.8-1.4c0,0-0.6-2,3-2.8,0,0,6.4-2.2,6.8-2.8,0,0,20.2-4.2,27.4-0.6,0,0,9.2,2.6,15.4,8.8,0,0,11.2,3.2,14.4,2.2,0,0,8.8,2.2,9.2,4,0,0,5.8,3,4,5.6,0,0,0.4,1.6-5.6,4.2z"/>
+  </g>
+  <g id="g422" fill="#cc7226">
+   <path id="path424" d="m168.33-108.51c0.81,0.63,1.83,0.73,2.43,1.54,0.24,0.31-0.05,0.64-0.37,0.74-1.04,0.31-2.1-0.26-3.24,0.33-0.4,0.21-1.04,0.03-1.6-0.12-1.63-0.44-3.46-0.47-5.15,0.22-1.98-1.13-4.34-0.54-6.42-1.55-0.06-0.02-0.28,0.32-0.36,0.3-3.04-1.15-6.79-0.87-9.22-3.15-2.43-0.41-4.78-0.87-7.21-1.55-1.82-0.51-3.23-1.5-4.85-2.33-1.38-0.71-2.83-1.23-4.37-1.61-1.86-0.45-3.69-0.34-5.58-0.86-0.1-0.02-0.29,0.32-0.37,0.3-0.32-0.11-0.62-0.69-0.79-0.64-1.68,0.52-3.17-0.45-4.83-0.11-1.18-1.22-2.9-0.98-4.45-1.42-2.97-0.85-6.12,0.42-9.15-0.58,4.11-1.84,8.8-0.61,12.86-2.68,2.33-1.18,4.99-0.08,7.56-0.84,0.49-0.15,1.18-0.35,1.58,0.32,0.14-0.14,0.32-0.37,0.38-0.35,2.44,1.16,4.76,2.43,7.24,3.5,0.34,0.15,0.88-0.09,1.13,0.12,1.52,1.21,3.46,1.11,4.85,2.33,1.7-0.5,3.49-0.12,5.22-0.75,0.08-0.02,0.31,0.32,0.34,0.3,1.14-0.75,2.29-0.48,3.18-0.18,0.34,0.12,1,0.37,1.31,0.44,1.12,0.27,1.98,0.75,3.16,0.94,0.11,0.02,0.3-0.32,0.37-0.3,1.12,0.44,2.16,0.39,2.82,1.55,0.14-0.14,0.3-0.37,0.38-0.35,1.03,0.34,1.68,1.1,2.78,1.34,0.48,0.1,1.1,0.73,1.67,0.91,2.39,0.73,4.24,2.26,6.43,3.15,0.76,0.31,1.64,0.55,2.27,1.04z"/>
+  </g>
+  <g id="g426" fill="#cc7226">
+   <path id="path428" d="m91.696-122.74c-2.518-1.72-4.886-2.83-7.328-4.62-0.181-0.13-0.541,0.04-0.743-0.08-1.007-0.61-1.895-1.19-2.877-1.89-0.539-0.38-1.36-0.37-1.868-0.63-2.544-1.29-5.173-1.85-7.68-3.04,0.682-0.64,1.804-0.39,2.4-1.2,0.195,0.28,0.433,0.56,0.786,0.37,1.678-0.9,3.528-1.05,5.204-0.96,1.704,0.09,3.424,0.39,5.199,0.67,0.307,0.04,0.506,0.56,0.829,0.66,2.228,0.66,4.617,0.14,6.736,0.98,1.591,0.63,3.161,1.45,4.4,2.72,0.252,0.26-0.073,0.57-0.353,0.76,0.388-0.11,0.661,0.1,0.772,0.41,0.084,0.24,0.084,0.54,0,0.78-0.112,0.31-0.391,0.41-0.765,0.46-1.407,0.19,0.365-1.19-0.335-0.74-1.273,0.82-0.527,2.22-1.272,3.49-0.28-0.19-0.51-0.41-0.4-0.8,0.234,0.52-0.368,0.81-0.536,1.13-0.385,0.72-1.284,2.14-2.169,1.53z"/>
+  </g>
+  <g id="g430" fill="#cc7226">
+   <path id="path432" d="m59.198-115.39c-3.154-0.79-6.204-0.68-9.22-1.96-0.067-0.02-0.29,0.32-0.354,0.3-1.366-0.6-2.284-1.56-3.36-2.61-0.913-0.89-2.571-0.5-3.845-0.99-0.324-0.12-0.527-0.63-0.828-0.67-1.219-0.16-2.146-1.11-3.191-1.68,2.336-0.8,4.747-0.76,7.209-1.15,0.113-0.02,0.258,0.31,0.391,0.31,0.136,0,0.266-0.23,0.4-0.36,0.195,0.28,0.497,0.61,0.754,0.35,0.548-0.54,1.104-0.35,1.644-0.31,0.144,0.01,0.269,0.32,0.402,0.32,0.136,0,0.267-0.32,0.4-0.32,0.136,0,0.267,0.32,0.4,0.32,0.136,0,0.266-0.23,0.4-0.36,0.692,0.78,1.577,0.23,2.399,0.41,1.038,0.22,1.305,1.37,2.379,1.67,4.715,1.3,8.852,3.45,13.215,5.54,0.307,0.14,0.517,0.39,0.407,0.78,0.267,0,0.58-0.09,0.77,0.04,1.058,0.74,2.099,1.28,2.796,2.38,0.216,0.34-0.113,0.75-0.346,0.7-4.429-1-8.435-1.61-12.822-2.71z"/>
+  </g>
+  <g id="g434" fill="#cc7226">
+   <path id="path436" d="m45.338-71.179c-1.592-1.219-2.176-3.25-3.304-5.042-0.214-0.34,0.06-0.654,0.377-0.743,0.56-0.159,1.103,0.319,1.512,0.521,1.745,0.862,3.28,2.104,5.277,2.243,1.99,2.234,6.25,2.619,6.257,6,0.001,0.859-1.427-0.059-1.857,0.8-2.451-1.003-4.84-0.9-7.22-2.367-0.617-0.381-0.287-0.834-1.042-1.412z"/>
+  </g>
+  <g id="g438" fill="#cc7226">
+   <path id="path440" d="m17.8-123.76c0.135,0,7.166,0.24,7.149,0.35-0.045,0.31-7.775,1.36-8.139,1.19-0.164-0.08-7.676,2.35-7.81,2.22,0.268-0.14,8.534-3.76,8.8-3.76z"/>
+  </g>
+  <g id="g442" fill="#000">
+   <path id="path444" d="m33.2-114s-14.8,1.8-19.2,3-23,8.8-26,10.8c0,0-13.4,5.4-30.4,25.4,0,0,7.6-3.4,9.8-6.2,0,0,13.6-12.6,13.4-10,0,0,12.2-8.6,11.6-6.4,0,0,24.4-11.2,22.4-8,0,0,21.6-4.6,20.6-2.6,0,0,18.8,4.4,16,4.6,0,0-5.8,1.2,0.6,4.8,0,0-3.4,4.4-8.8,0.4s-2.4-1.8-7.4-0.8c0,0-2.6,0.8-7.2-3.2,0,0-5.6-4.6-14.4-1,0,0-30.6,12.6-32.6,13.2,0,0-3.6,2.8-6,6.4,0,0-5.8,4.4-8.8,5.8,0,0-12.8,11.6-14,13,0,0-3.4,5.2-4.2,5.6,0,0,6.4-3.8,8.4-5.8,0,0,14-10,19.4-10.8,0,0,4.4-3,5.2-4.4,0,0,14.4-9.2,18.6-9.2,0,0,9.2,5.2,11.6-1.8,0,0,5.8-1.8,11.4-0.6,0,0,3.2-2.6,2.4-4.8,0,0,1.6-1.8,2.6,2,0,0,3.4,3.6,8.2,1.6,0,0,4-0.2,2,2.2,0,0-4.4,3.8-16.2,4,0,0-12.4,0.6-28.8,8.2,0,0-29.8,10.4-39,20.8,0,0-6.4,8.8-11.8,10,0,0-5.8,0.8-11.8,8.2,0,0,9.8-5.8,18.8-5.8,0,0,4-2.4,0.2,1.2,0,0-3.6,7.6-2,13,0,0-0.6,5.2-1.4,6.8,0,0-7.8,12.8-7.8,15.2s1.2,12.2,1.6,12.8-1-1.6,2.8,0.8,6.6,4,7.4,6.8-2-5.4-2.2-7.2-4.4-9-3.6-11.4c0,0,1,1,1.8,2.4,0,0-0.6-0.6,0-4.2,0,0,0.8-5.2,2.2-8.4s3.4-7,3.8-7.8,0.4-6.6,1.8-4l3.4,2.6s-2.8-2.6-0.6-4.8c0,0-1-5.6,0.8-8.2,0,0,7-8.4,8.6-9.4s0.2-0.6,0.2-0.6,6-4.2,0.2-2.6c0,0-4,1.6-7,1.6,0,0-7.6,2-3.6-2.2s14-9.6,17.8-9.4l0.8,1.6,11.2-2.4-1.2,0.8s-0.2-0.2,4-0.6,10,1,11.4-0.8,4.8-2.8,4.4-1.4-0.6,3.4-0.6,3.4,5-5.8,4.4-3.6-8.8,7.4-10.2,13.6l10.4-8.2,3.6-3s3.6,2.2,3.8,0.6,4.8-7.4,6-7.2,3.2-2.6,3,0,7.4,8,7.4,8,3.2-1.8,4.6-0.4,5.6-19.8,5.6-19.8l25-10.6,43.6-3.4-16.999-6.8-61.001-11.4z"/>
+  </g>
+  <g id="g446" stroke-width="2" stroke="#4c0000">
+   <path id="path448" d="m51.4,85s-15-16.8-23.4-19.4c0,0-13.4-6.8-38,1"/>
+  </g>
+  <g id="g450" stroke-width="2" stroke="#4c0000">
+   <path id="path452" d="m24.8,64.2s-25.2-8-40.6-3.8c0,0-18.4,2-26.8,15.8"/>
+  </g>
+  <g id="g454" stroke-width="2" stroke="#4c0000">
+   <path id="path456" d="m21.2,63s-17-7.2-31.8-9.4c0,0-16.6-2.6-33.2,4.6,0,0-12.2,6-17.6,16.2"/>
+  </g>
+  <g id="g458" stroke-width="2" stroke="#4c0000">
+   <path id="path460" d="m22.2,63.4s-15.4-11-16.4-12.4c0,0-7-11-20-11.4,0,0-21.4,0.8-38.6,8.8"/>
+  </g>
+  <g id="g462" fill="#000">
+   <path id="path464" d="M20.895,54.407c1.542,1.463,28.505,30.393,28.505,30.393,35.2,36.6,7.2,2.4,7.2,2.4-7.6-4.8-16.8-23.6-16.8-23.6-1.2-2.8,14,7.2,14,7.2,4,0.8,17.6,20,17.6,20-6.8-2.4-2,4.8-2,4.8,2.8,2,23.201,17.6,23.201,17.6,3.6,4,7.599,5.6,7.599,5.6,14-5.2,7.6,8,7.6,8,2.4,6.8,8-4.8,8-4.8,11.2-16.8-5.2-14.4-5.2-14.4-30,2.8-36.8-13.2-36.8-13.2-2.4-2.4,6.4,0,6.4,0,8.401,2-7.2-12.4-7.2-12.4,2.4,0,11.6,6.8,11.6,6.8,10.401,9.2,12.401,7.2,12.401,7.2,17.999-8.8,28.399-1.2,28.399-1.2,2,1.6-3.6,8.4-2,13.6s6.4,17.6,6.4,17.6c-2.4,1.6-2,12.4-2,12.4,16.8,23.2,7.2,21.2,7.2,21.2-15.6-0.4-0.8,7.2-0.8,7.2,3.2,2,12,9.2,12,9.2-2.8-1.2-4.4,4-4.4,4,4.8,4,2,8.8,2,8.8-6,1.2-7.2,5.2-7.2,5.2,6.8,8-3.2,8.4-3.2,8.4,3.6,4.4-1.2,16.4-1.2,16.4-4.8,0-11.2,5.6-11.2,5.6,2.4,4.8-8,10.4-8,10.4-8.4,1.6-5.6,8.4-5.6,8.4-7.999,6-10.399,22-10.399,22-0.8,10.4-3.2,13.6,2,11.6,5.199-2,4.399-14.4,4.399-14.4-4.799-15.6,38-31.6,38-31.6,4-1.6,4.8-6.8,4.8-6.8,2,0.4,10.8,8,10.8,8,7.6,11.2,8,2,8,2,1.2-3.6-0.4-9.6-0.4-9.6,6-21.6-8-28-8-28-10-33.6,4-25.2,4-25.2,2.8,5.6,13.6,10.8,13.6,10.8l3.6-2.4c-1.6-4.8,6.8-10.8,6.8-10.8,2.8,6.4,8.8-1.6,8.8-1.6,3.6-24.4,16-10,16-10,4,1.2,5.2-5.6,5.2-5.6,3.6-10.4,0-24,0-24,3.6-0.4,13.2,5.6,13.2,5.6,2.8-3.6-6.4-20.4-2.4-18s8.4,4,8.4,4c0.8-2-9.2-14.4-9.2-14.4-4.4-2.8-9.6-23.2-9.6-23.2,7.2,3.6-2.8-11.6-2.8-11.6,0-3.2,6-14.4,6-14.4-0.8-6.8,0-6.4,0-6.4,2.8,1.2,10.8,2.8,4-3.6s0.8-11.2,0.8-11.2c4.4-2.8-9.2-2.4-9.2-2.4-5.2-4.4-4.8-8.4-4.8-8.4,8,2-6.4-12.4-8.8-16s7.2-8.8,7.2-8.8c13.2-3.6,1.6-6.8,1.6-6.8-19.6,0.4-8.8-10.4-8.8-10.4,6,0.4,4.4-2,4.4-2-5.2-1.2-14.8-7.6-14.8-7.6-4-3.6-0.4-2.8-0.4-2.8,16.8,1.2-12-10-12-10,8,0-10-10.4-10-10.4-2-1.6-5.2-9.2-5.2-9.2-6-5.2-10.8-12-10.8-12-0.4-4.4-5.2-9.2-5.2-9.2-11.6-13.6-17.2-13.2-17.2-13.2-14.8-3.6-20-2.8-20-2.8l-52.8,4.4c-26.4,12.8-18.6,33.8-18.6,33.8,6.4,8.4,15.6,4.6,15.6,4.6,4.6-6.2,16.2-4,16.2-4,20.401,3.2,17.801-0.4,17.801-0.4-2.4-4.6-18.601-10.8-18.801-11.4s-9-4-9-4c-3-1.2-7.4-10.4-7.4-10.4-3.2-3.4,12.6,2.4,12.6,2.4-1.2,1,6.2,5,6.2,5,17.401-1,28.001,9.8,28.001,9.8,10.799,16.6,10.999,8.4,10.999,8.4,2.8-9.4-9-30.6-9-30.6,0.4-2,8.6,4.6,8.6,4.6,1.4-2,2.2,3.8,2.2,3.8,0.2,2.4,4,10.4,4,10.4,2.8,13,6.4,5.6,6.4,5.6l4.6,9.4c1.4,2.6-4.6,10.2-4.6,10.2-0.2,2.8,0.6,2.6-5,10.2s-2.2,12-2.2,12c-1.4,6.6,7.4,6.2,7.4,6.2,2.6,2.2,6,2.2,6,2.2,1.8,2,4.2,1.4,4.2,1.4,1.6-3.8,7.8-1.8,7.8-1.8,1.4-2.4,9.6-2.8,9.6-2.8,1-2.6,1.4-4.2,4.8-4.8s-21.2-43.6-21.2-43.6c6.4-0.8-1.8-13.2-1.8-13.2-2.2-6.6,9.2,8,11.4,9.4s3.2,3.6,1.6,3.4-3.4,2-2,2.2,14.4,15.2,17.8,25.4,9.4,14.2,15.6,20.2,5.4,30.2,5.4,30.2c-0.4,8.8,5.6,19.4,5.6,19.4,2,3.8-2.2,22-2.2,22-2,2.2-0.6,3-0.6,3,1,1.2,7.8,14.4,7.8,14.4-1.8-0.2,1.8,3.4,1.8,3.4,5.2,6-1.2,3-1.2,3-6-1.6,1,8.2,1,8.2,1.2,1.8-7.8-2.8-7.8-2.8-9.2-0.6,2.4,6.6,2.4,6.6,8.6,7.2-2.8,2.8-2.8,2.8-4.6-1.8-1.4,5-1.4,5,3.2,1.6,20.4,8.6,20.4,8.6,0.4,3.8-2.6,8.8-2.6,8.8,0.4,4-1.8,7.4-1.8,7.4-1.2,8.2-1.8,9-1.8,9-4.2,0.2-11.6,14-11.6,14-1.8,2.6-12,14.6-12,14.6-2,7-20-0.2-20-0.2-6.6,3.4-4.6,0-4.6,0-0.4-2.2,4.4-8.2,4.4-8.2,7-2.6,4.4-13.4,4.4-13.4,4-1.4-7.2-4.2-7-5.4s6-2.6,6-2.6c8-2,3.6-4.4,3.6-4.4-0.6-4,2.4-9.6,2.4-9.6,11.6-0.8,0-17,0-17-10.8-7.6-11.8-13.4-11.8-13.4,12.6-8.2,4.4-20.6,4.6-24.2s1.4-25.2,1.4-25.2c-2-6.2-5-19.8-5-19.8,2.2-5.2,9.6-17.8,9.6-17.8,2.8-4.2,11.6-9,9.4-12s-10-1.2-10-1.2c-7.8-1.4-7.2,3.8-7.2,3.8-1.6,1-2.4,6-2.4,6-0.72,7.933-9.6,14.2-9.6,14.2-11.2,6.2-2,10.2-2,10.2,6,6.6-3.8,6.8-3.8,6.8-11-1.8-2.8,8.4-2.8,8.4,10.8,12.8,7.8,15.6,7.8,15.6-10.2,1,2.4,10.2,2.4,10.2s-0.8-2-0.6-0.2,3.2,6,4,8-3.2,2.2-3.2,2.2c0.6,9.6-14.8,5.4-14.8,5.4l-1.6,0.2c-1.6,0.2-12.8-0.6-18.6-2.8s-12.599-2.2-12.599-2.2-4,1.8-11.601,1.6c-7.6-0.2-15.6,2.6-15.6,2.6-4.4-0.4,4.2-4.8,4.4-4.6s5.8-5.4-2.2-4.8c-21.797,1.635-32.6-8.6-32.6-8.6-2-1.4-4.6-4.2-4.6-4.2-10-2,1.4,12.4,1.4,12.4,1.2,1.4-0.2,2.4-0.2,2.4-0.8-1.6-8.6-7-8.6-7-2.811-0.973-4.174-2.307-6.505-4.793z"/>
+  </g>
+  <g id="g466" fill="#4c0000">
+   <path id="path468" d="m-3,42.8s11.6,5.6,14.2,8.4,16.6,14.2,16.6,14.2-5.4-2-8-3.8-13.4-10-13.4-10-3.8-6-9.4-8.8z"/>
+  </g>
+  <g id="g470" fill="#99cc32">
+   <path id="path472" d="M-61.009,11.603c0.337-0.148-0.187-2.86-0.391-3.403-1.022-2.726-10-4.2-10-4.2-0.227,1.365-0.282,2.961-0.176,4.599,0,0,4.868,5.519,10.567,3.004z"/>
+  </g>
+  <g id="g474" fill="#659900">
+   <path id="path476" d="M-61.009,11.403c-0.449,0.158-0.015-2.734-0.191-3.203-1.022-2.726-10.2-4.3-10.2-4.3-0.227,1.365-0.282,2.961-0.176,4.599,0,0,4.268,5.119,10.567,2.904z"/>
+  </g>
+  <g id="g478" fill="#000">
+   <path id="path480" d="m-65.4,11.546c-0.625,0-1.131-1.14-1.131-2.546,0-1.405,0.506-2.545,1.131-2.545s1.132,1.14,1.132,2.545c0,1.406-0.507,2.546-1.132,2.546z"/>
+  </g>
+  <g id="g482" fill="#000">
+   <path id="path484" d="M-65.4,9z"/>
+  </g>
+  <g id="g486" fill="#000">
+   <path id="path488" d="m-111,109.6s-5.6,10,19.2,4c0,0,14-1.2,16.4-3.6,1.2,0.8,9.566,3.73,12.4,4.4,6.8,1.6,15.2-8.4,15.2-8.4s4.6-10.5,7.4-10.5-0.4,1.6-0.4,1.6-6.6,10.1-6.2,11.7c0,0-5.2,20-21.2,20.8,0,0-16.15,0.95-14.8,6.8,0,0,8.8-2.4,11.2,0,0,0,10.8-0.4,2.8,6l-6.8,11.6s0.14,3.92-10,0.4c-9.8-3.4-20.1-16.3-20.1-16.3s-15.95-14.55-5.1-28.5z"/>
+  </g>
+  <g id="g490" fill="#e59999">
+   <path id="path492" d="m-112.2,113.6s-2,9.6,34.8-0.8l6.8,0.8c2.4,0.8,14.4,3.6,16.4,2.4,0,0-7.2,13.6-18.8,12,0,0-13.2,1.6-12.8,6.4,0,0,4,7.2,8.8,9.6,0,0,2.8,2.4,2.4,5.6s-3.2,4.8-5.2,5.6-5.2-2.4-6.8-2.4-10-6.4-14.4-11.2-12.8-16.8-12.4-19.6,1.2-8.4,1.2-8.4z"/>
+  </g>
+  <g id="g494" fill="#b26565">
+   <path id="path496" d="m-109,131.05c2.6,3.95,5.8,8.15,8,10.55,4.4,4.8,12.8,11.2,14.4,11.2s4.8,3.2,6.8,2.4,4.8-2.4,5.2-5.6-2.4-5.6-2.4-5.6c-3.066-1.53-5.806-5.02-7.385-7.35,0,0,0.185,2.55-5.015,1.75s-10.4-3.6-12-6.8-4-5.6-2.4-2,4,7.2,5.6,7.6,1.2,1.6-1.2,1.2-5.2-0.8-9.6-6z"/>
+  </g>
+  <g id="g498" fill="#992600">
+   <path id="path500" d="m-111.6,110s1.8-13.6,3-17.6c0,0-0.8-6.8,1.6-11s4.4-10.4,7.4-15.8,3.2-9.4,7.2-11,10-10.2,12.8-11.2,2.6-0.2,2.6-0.2,6.8-14.8,20.4-10.8c0,0-16.2-2.8-0.4-12.2,0,0-4.8,1.1-1.5-5.9,2.201-4.668,1.7,2.1-9.3,13.9,0,0-5,8.6-10.2,11.6s-17.2,10-18.4,13.8-4.4,9.6-6.4,11.2-4.8,5.8-5.2,9.2c0,0-1.2,4-2.6,5.2s-1.6,4.4-1.6,6.4-2,4.8-1.8,7.2c0,0,0.8,19,0.4,21l2-3.8z"/>
+  </g>
+  <g id="g502" fill="#FFF">
+   <path id="path504" d="m-120.2,114.6s-2-1.4-6.4,4.6c0,0,7.3,33,7.3,34.4,0,0,1.1-2.1-0.2-9.3s-2.2-19.9-2.2-19.9l1.5-9.8z"/>
+  </g>
+  <g id="g506" fill="#992600">
+   <path id="path508" d="m-98.6,54s-17.6,3.2-17.2,32.4l-0.8,24.8s-1.2-25.6-2.4-27.2,2.8-12.8-0.4-6.8c0,0-14,14-6,35.2,0,0,1.5,3.3-1.5-1.3,0,0-4.6-12.6-3.5-19,0,0,0.2-2.2,2.1-5,0,0,8.6-11.7,11.3-14,0,0,1.8-14.4,17.2-19.6,0,0,5.7-2.3,1.2,0.5z"/>
+  </g>
+  <g id="g510" fill="#000">
+   <path id="path512" d="m40.8-12.2c0.66-0.354,0.651-1.324,1.231-1.497,1.149-0.344,1.313-1.411,1.831-2.195,0.873-1.319,1.066-2.852,1.648-4.343,0.272-0.7,0.299-1.655-0.014-2.315-1.174-2.481-1.876-4.93-3.318-7.356-0.268-0.45-0.53-1.244-0.731-1.842-0.463-1.384-1.72-2.375-2.58-3.695-0.288-0.441,0.237-1.366-0.479-1.45-0.897-0.105-2.346-0.685-2.579,0.341-0.588,2.587,0.423,5.11,1.391,7.552-0.782,0.692-0.448,1.613-0.296,2.38,0.71,3.606-0.488,6.958-1.249,10.432-0.023,0.104,0.319,0.302,0.291,0.364-1.222,2.686-2.674,5.131-4.493,7.512-0.758,0.992-1.63,1.908-2.127,2.971-0.368,0.787-0.776,1.753-0.526,2.741-3.435,2.78-5.685,6.625-8.296,10.471-0.462,0.68-0.171,1.889,0.38,2.158,0.813,0.398,1.769-0.626,2.239-1.472,0.389-0.698,0.742-1.348,1.233-1.991,0.133-0.175-0.046-0.594,0.089-0.715,2.633-2.347,4.302-5.283,6.755-7.651,1.95-0.329,3.487-1.327,5.235-2.34,0.308-0.179,0.832,0.07,1.122-0.125,1.753-1.177,1.751-3.213,1.857-5.123,0.05-0.884,0.246-2.201,1.386-2.812z"/>
+  </g>
+  <g id="g514" fill="#000">
+   <path id="path516" d="m31.959-16.666c0.124-0.077-0.031-0.5,0.078-0.716,0.162-0.324,0.565-0.512,0.727-0.836,0.109-0.216-0.054-0.596,0.082-0.738,2.333-2.447,2.59-5.471,1.554-8.444,1.024-0.62,1.085-1.882,0.66-2.729-0.853-1.7-1.046-3.626-2.021-5.169-0.802-1.269-2.38-2.513-3.751-1.21-0.421,0.4-0.742,1.187-0.464,1.899,0.064,0.163,0.349,0.309,0.322,0.391-0.107,0.324-0.653,0.548-0.659,0.82-0.03,1.496-0.984,3.007-0.354,4.336,0.772,1.629,1.591,3.486,2.267,5.262-1.234,2.116-0.201,4.565-1.954,6.442-0.136,0.146-0.127,0.532-0.005,0.734,0.292,0.486,0.698,0.892,1.184,1.184,0.202,0.121,0.55,0.123,0.75-0.001,0.578-0.362,0.976-0.849,1.584-1.225z"/>
+  </g>
+  <g id="g518" fill="#000">
+   <path id="path520" d="m94.771-26.977c1.389,1.792,1.679,4.587-0.37,5.977,0.55,3.309,3.901,1.33,5.999,0.8-0.11-0.388,0.12-0.732,0.4-0.737,1.06-0.015,1.74-1.047,2.8-0.863,0.44-1.557,2.07-2.259,2.72-3.639,1.72-3.695,1.13-7.968-1.45-11.214-0.2-0.254,0.01-0.771-0.11-1.133-0.76-2.211-2.82-2.526-4.76-3.214-1.176-3.875-1.837-7.906-3.599-11.6-1.614-0.25-2.312-1.989-3.649-2.709-1.333-0.719-1.901,0.86-1.86,1.906,0.007,0.205,0.459,0.429,0.289,0.794-0.076,0.164-0.336,0.275-0.336,0.409,0.001,0.135,0.222,0.266,0.356,0.4-0.918,0.82-2.341,1.297-2.636,2.442-0.954,3.71,1.619,6.835,3.287,10.036,0.591,1.135-0.145,2.406-0.905,3.614-0.438,0.695-0.33,1.822-0.054,2.678,0.752,2.331,2.343,4.07,3.878,6.053z"/>
+  </g>
+  <g id="g522" fill="#000">
+   <path id="path524" d="m57.611-8.591c-1.487,1.851-4.899,4.42-1.982,6.348,0.194,0.129,0.564,0.133,0.737-0.001,2.021-1.565,4.024-2.468,6.46-3.05,0.124-0.029,0.398,0.438,0.767,0.277,1.613-0.703,3.623-0.645,4.807-1.983,3.767,0.224,7.332-0.892,10.723-2.2,1.161-0.448,2.431-1.007,3.632-1.509,1.376-0.576,2.58-1.504,3.692-2.645,0.133-0.136,0.487-0.046,0.754-0.046-0.04-0.863,0.922-0.99,1.169-1.612,0.092-0.232-0.058-0.628,0.075-0.73,2.138-1.63,3.058-3.648,1.889-6.025-0.285-0.578-0.534-1.196-1.1-1.672-1.085-0.911-2.187-0.057-3.234-0.361-0.159,0.628-0.888,0.456-1.274,0.654-0.859,0.439-2.192-0.146-3.051,0.292-1.362,0.695-2.603,0.864-4.025,1.241-0.312,0.082-1.09-0.014-1.25,0.613-0.134-0.134-0.282-0.368-0.388-0.346-1.908,0.396-3.168,0.61-4.469,2.302-0.103,0.133-0.545-0.046-0.704,0.089-0.957,0.808-1.362,2.042-2.463,2.714-0.201,0.123-0.553-0.045-0.747,0.084-0.646,0.431-1.013,1.072-1.655,1.519-0.329,0.229-0.729-0.096-0.697-0.352,0.245-1.947,0.898-3.734,0.323-5.61,2.077-2.52,4.594-4.469,6.4-7.2,0.015-2.166,0.707-4.312,0.594-6.389-0.01-0.193-0.298-0.926-0.424-1.273-0.312-0.854,0.594-1.92-0.25-2.644-1.404-1.203-2.696-0.327-3.52,1.106-1.838,0.39-3.904,1.083-5.482-0.151-1.007-0.787-1.585-1.693-2.384-2.749-0.985-1.302-0.65-2.738-0.58-4.302,0.006-0.128-0.309-0.264-0.309-0.398,0.001-0.135,0.221-0.266,0.355-0.4-0.706-0.626-0.981-1.684-2-2,0.305-1.092-0.371-1.976-1.242-2.278-1.995-0.691-3.672,1.221-5.564,1.294-0.514,0.019-0.981-1.019-1.63-1.344-0.432-0.216-1.136-0.249-1.498,0.017-0.688,0.504-1.277,0.618-2.035,0.823-1.617,0.436-2.895,1.53-4.375,2.385-1.485,0.857-2.44,2.294-3.52,3.614-0.941,1.152-1.077,3.566,0.343,4.066,1.843,0.65,3.147-2.053,5.113-1.727,0.312,0.051,0.518,0.362,0.408,0.75,0.389,0.109,0.607-0.12,0.8-0.4,0.858,1.019,2.022,1.356,2.96,2.229,0.97,0.904,2.716,0.486,3.731,1.483,1.529,1.502,0.97,4.183,2.909,5.488-0.586,1.313-1.193,2.59-1.528,4.017-0.282,1.206,0.712,2.403,1.923,2.312,1.258-0.094,1.52-0.853,2.005-1.929,0.267,0.267,0.736,0.564,0.695,0.78-0.457,2.387-1.484,4.38-1.942,6.811-0.059,0.317-0.364,0.519-0.753,0.409-0.468,4.149-4.52,6.543-7.065,9.708-0.403,0.502-0.407,1.751,0.002,2.154,1.403,1.387,3.363-0.159,5.063-0.662,0.213-1.206,1.072-2.148,2.404-2.092,0.256,0.01,0.491-0.532,0.815-0.662,0.348-0.138,0.85,0.086,1.136-0.112,1.729-1.195,3.137-2.301,4.875-3.49,0.192-0.131,0.536,0.028,0.752-0.08,0.325-0.162,0.512-0.549,0.835-0.734,0.348-0.2,0.59,0.09,0.783,0.37-0.646,0.349-0.65,1.306-1.232,1.508-0.775,0.268-1.336,0.781-2.01,1.228-0.292,0.193-0.951-0.055-1.055,0.124-0.598,1.028-1.782,1.466-2.492,2.349z"/>
+  </g>
+  <g id="g526" fill="#000">
+   <path id="path528" d="m2.2-58s-9.238-2.872-20.4,22.8c0,0-2.4,5.2-4.8,7.2s-13.6,5.6-15.6,9.6l-10.4,16s14.8-16,18-18.4c0,0,8-8.4,4.8-1.6,0,0-14,10.8-12.8,20,0,0-5.6,14.4-6.4,16.4,0,0,16-32,18.4-33.2s3.6-1.2,2.4,2.4-1.6,20-4.4,22c0,0,8-20.4,7.2-23.6,0,0,3.2-3.6,5.6,1.6l-1.2,16,4.4,12s-2.4-11.2-0.8-26.8c0,0-2-10.4,2-4.8s13.6,11.6,13.6,16.4c0,0-5.2-17.6-14.4-22.4l-4,6-1.2-2s-3.6-0.8,0.8-7.6,4-7.6,4-7.6,6.4,7.2,8,7.2c0,0,13.2-7.6,14.4,16.8,0,0,6.8-14.4-2.4-21.2,0,0-14.8-2-13.6-7.2l7.2-12.4c3.6-5.2,2-2.4,2-2.4z"/>
+  </g>
+  <g id="g530" fill="#000">
+   <path id="path532" d="m-17.8-41.6-16,5.2-7.2,9.6s17.2-10,21.2-11.2,2-3.6,2-3.6z"/>
+  </g>
+  <g id="g534" fill="#000">
+   <path id="path536" d="m-57.8-35.2s-2,1.2-2.4,4-2.8,3.2-2,6,2.8,5.2,2.8,1.2,1.6-6,2.4-7.2,2.4-5.6-0.8-4z"/>
+  </g>
+  <g id="g538" fill="#000">
+   <path id="path540" d="m-66.6,26s-8.4-4-11.6-7.6-2.748,1.566-7.6,1.2c-5.847-0.441-4.8-16.4-4.8-16.4l-4,7.6s-1.2,14.4,6.8,12c3.907-1.172,5.2,0.4,3.6,1.2s5.6,1.2,2.8,2.8,11.6-3.6,9.2,6.8l5.6-7.6z"/>
+  </g>
+  <g id="g542" fill="#000">
+   <path id="path544" d="m-79.2,40.4s-15.4,4.4-19-5.2c0,0-4.8,2.4-2.6,5.4s3.4,3.4,3.4,3.4,5.4,1.2,4.8,2-3,4.2-3,4.2,10.2-6,16.4-9.8z"/>
+  </g>
+  <g id="g546" fill="#FFF">
+   <path id="path548" d="m149.2,118.6c-0.43,2.14-2.1,2.94-4,3.6-1.92-0.96-4.51-4.06-6.4-2-0.47-0.48-1.25-0.54-1.6-1.2-0.46-0.9-0.19-1.94-0.53-2.74-0.55-1.28-1.25-2.64-1.07-4.06,1.81-0.71,2.4-2.62,1.93-4.38-0.07-0.26-0.5-0.45-0.3-0.8,0.19-0.33,0.5-0.55,0.77-0.82-0.13,0.14-0.28,0.37-0.39,0.35-0.61-0.11-0.49-0.75-0.36-1.13,0.59-1.75,2.6-2.01,3.95-0.82,0.26-0.56,0.77-0.37,1.2-0.4-0.05-0.58,0.36-1.11,0.56-1.53,0.52-1.09,2.14,0.01,2.94-0.6,1.08-0.83,2.14-1.52,3.22-0.92,1.81,1.01,3.52,2.22,4.72,3.97,0.57,0.83,0.81,2.11,0.75,3.07-0.04,0.65-1.42,0.29-1.76,1.22-0.65,1.75,1.19,2.27,1.94,3.61,0.2,0.35-0.06,0.65-0.38,0.75-0.41,0.13-1.19-0.06-1.06,0.39,0.98,3.19-1.78,3.87-4.13,4.44z"/>
+  </g>
+  <g id="g550" fill="#FFF">
+   <path id="path552" d="m139.6,138.2c-0.01-1.74-1.61-3.49-0.4-5.2,0.14,0.14,0.27,0.36,0.4,0.36,0.14,0,0.27-0.22,0.4-0.36,1.5,2.22,5.15,3.14,5.01,5.99-0.03,0.45-1.11,1.37-0.21,2.01-1.81,1.35-1.87,3.72-2.8,5.6-1.24-0.28-2.45-0.65-3.6-1.2,0.35-1.48,0.24-3.17,1.06-4.49,0.43-0.7,0.14-1.78,0.14-2.71z"/>
+  </g>
+  <g id="g554" fill="#CCC">
+   <path id="path556" d="m-26.6,129.2s-16.858,10.14-2.8-5.2c8.8-9.6,18.8-15.2,18.8-15.2s10.4-4.4,14-5.6,18.8-6.4,22-6.8,12.8-4.4,19.6-0.4,14.8,8.4,14.8,8.4-16.4-8.4-20-6-10.8,2-16.8,5.2c0,0-14.8,4.4-18,6.4s-13.6,13.6-15.2,12.8,0.4-1.2,1.6-4-0.8-4.4-8.8,2-9.2,8.4-9.2,8.4z"/>
+  </g>
+  <g id="g558" fill="#000">
+   <path id="path560" d="m-19.195,123.23s1.41-13.04,9.888-11.37c0,0,8.226-4.17,10.948-6.14,0,0,8.139-1.7,9.449-2.32,18.479-8.698,33.198-4.179,33.745-5.299,0.546-1.119,20.171,5.999,23.78,10.079,0.391,0.45-10.231-5.59-19.929-7.48-8.273-1.617-29.875,0.24-40.781,5.78-2.973,1.51-11.918,7.29-14.449,7.18s-12.651,9.57-12.651,9.57z"/>
+  </g>
+  <g id="g562" fill="#CCC">
+   <path id="path564" d="m-23,148.8s-15.2-2.4,1.6-4c0,0,18-2,22-7.2,0,0,13.6-9.2,16.4-9.6s32.8-7.6,33.2-10,6-2.4,7.6-1.6,0.8,2-2,2.8-34,17.2-40.4,18.4-18,8.8-22.8,10-15.6,1.2-15.6,1.2z"/>
+  </g>
+  <g id="g566" fill="#000">
+   <path id="path568" d="m-3.48,141.4s-8.582-0.83,0.019-1.64c0,0,8.816-3.43,10.864-6.09,0,0,6.964-4.71,8.397-4.92,1.434-0.2,15.394-3.89,15.599-5.12s34.271-13.81,38.691-10.62c2.911,2.1-6.99,0.43-16.624,4.84-1.355,0.62-35.208,15.2-38.485,15.82-3.277,0.61-9.216,4.5-11.674,5.12-2.457,0.61-6.787,2.61-6.787,2.61z"/>
+  </g>
+  <g id="g570" fill="#000">
+   <path id="path572" d="m-11.4,143.6s5.2-0.4,4,1.2-3.6,0.8-3.6,0.8l-0.4-2z"/>
+  </g>
+  <g id="g574" fill="#000">
+   <path id="path576" d="m-18.6,145.2s5.2-0.4,4,1.2-3.6,0.8-3.6,0.8l-0.4-2z"/>
+  </g>
+  <g id="g578" fill="#000">
+   <path id="path580" d="m-29,146.8s5.2-0.4,4,1.2-3.6,0.8-3.6,0.8l-0.4-2z"/>
+  </g>
+  <g id="g582" fill="#000">
+   <path id="path584" d="m-36.6,147.6s5.2-0.4,4,1.2-3.6,0.8-3.6,0.8l-0.4-2z"/>
+  </g>
+  <g id="g586" fill="#000">
+   <path id="path588" d="m1.8,108,3.2,1.6c-1.2,1.6-4.4,1.2-4.4,1.2l1.2-2.8z"/>
+  </g>
+  <g id="g590" fill="#000">
+   <path id="path592" d="m-8.2,113.6s6.506-2.14,4,1.2c-1.2,1.6-3.6,0.8-3.6,0.8l-0.4-2z"/>
+  </g>
+  <g id="g594" fill="#000">
+   <path id="path596" d="m-19.4,118.4s5.2-0.4,4,1.2-3.6,0.8-3.6,0.8l-0.4-2z"/>
+  </g>
+  <g id="g598" fill="#000">
+   <path id="path600" d="m-27,124.4s5.2-0.4,4,1.2-3.6,0.8-3.6,0.8l-0.4-2z"/>
+  </g>
+  <g id="g602" fill="#000">
+   <path id="path604" d="m-33.8,129.2s5.2-0.4,4,1.2-3.6,0.8-3.6,0.8l-0.4-2z"/>
+  </g>
+  <g id="g606" fill="#000">
+   <path id="path608" d="m5.282,135.6s6.921-0.53,5.324,1.6c-1.597,2.12-4.792,1.06-4.792,1.06l-0.532-2.66z"/>
+  </g>
+  <g id="g610" fill="#000">
+   <path id="path612" d="m15.682,130.8s6.921-0.53,5.324,1.6c-1.597,2.12-4.792,1.06-4.792,1.06l-0.532-2.66z"/>
+  </g>
+  <g id="g614" fill="#000">
+   <path id="path616" d="m26.482,126.4s6.921-0.53,5.324,1.6c-1.597,2.12-4.792,1.06-4.792,1.06l-0.532-2.66z"/>
+  </g>
+  <g id="g618" fill="#000">
+   <path id="path620" d="m36.882,121.6s6.921-0.53,5.324,1.6c-1.597,2.12-4.792,1.06-4.792,1.06l-0.532-2.66z"/>
+  </g>
+  <g id="g622" fill="#000">
+   <path id="path624" d="m9.282,103.6s6.921-0.53,5.324,1.6c-1.597,2.12-5.592,1.86-5.592,1.86l0.268-3.46z"/>
+  </g>
+  <g id="g626" fill="#000">
+   <path id="path628" d="m19.282,100.4s6.921-0.534,5.324,1.6c-1.597,2.12-5.992,1.86-5.992,1.86l0.668-3.46z"/>
+  </g>
+  <g id="g630" fill="#000">
+   <path id="path632" d="m-3.4,140.4s5.2-0.4,4,1.2-3.6,0.8-3.6,0.8l-0.4-2z"/>
+  </g>
+  <g id="g634" fill="#992600">
+   <path id="path636" d="m-76.6,41.2s-4.4,8.8-4.8,12c0,0,0.8-8.8,2-10.8s2.8-1.2,2.8-1.2z"/>
+  </g>
+  <g id="g638" fill="#992600">
+   <path id="path640" d="m-95,55.2s-3.2,14.4-2.8,17.2c0,0-1.2-11.6-0.8-12.8s3.6-4.4,3.6-4.4z"/>
+  </g>
+  <g id="g642" fill="#CCC">
+   <path id="path644" d="m-74.2-19.4-0.2,3.2-2.2,0.2s14.2,12.6,14.8,20.2c0,0,0.8-8.2-12.4-23.6z"/>
+  </g>
+  <g id="g646" fill="#000">
+   <path id="path648" d="m-70.216-18.135c-0.431-0.416-0.212-1.161-0.62-1.421-0.809-0.516,1.298-0.573,1.07-1.289-0.383-1.206-0.196-1.227-0.318-2.503-0.057-0.598,0.531-2.138,0.916-2.578,1.446-1.652,0.122-4.584,1.762-6.135,0.304-0.289,0.68-0.841,0.965-1.259,0.659-0.963,1.843-1.451,2.793-2.279,0.318-0.276,0.117-1.103,0.686-1.011,0.714,0.115,1.955-0.015,1.91,0.826-0.113,2.12-1.442,3.84-2.722,5.508,0.451,0.704-0.007,1.339-0.291,1.896-1.335,2.62-1.146,5.461-1.32,8.301-0.005,0.085-0.312,0.163-0.304,0.216,0.353,2.335,0.937,4.534,1.816,6.763,0.366,0.93,0.837,1.825,0.987,2.752,0.111,0.686,0.214,1.519-0.194,2.224,2.035,2.89,0.726,5.541,1.895,9.072,0.207,0.625,1.899,2.539,1.436,2.378-2.513-0.871-2.625-1.269-2.802-2.022-0.146-0.623-0.476-2-0.713-2.602-0.064-0.164-0.235-2.048-0.313-2.17-1.513-2.382-0.155-2.206-1.525-4.564-1.428-0.68-2.394-1.784-3.517-2.946-0.198-0.204,0.945-0.928,0.764-1.141-1.092-1.289-2.245-2.056-1.909-3.549,0.155-0.69,0.292-1.747-0.452-2.467z"/>
+  </g>
+  <g id="g650" fill="#000">
+   <path id="path652" d="m-73.8-16.4s0.4,6.8,2.8,8.4,1.2,0.8-2-0.4-2-2-2-2-2.8,0.4-0.4,2.4,6,4.4,4.4,4.4-9.2-4-9.2-6.8-1-6.9-1-6.9,1.1-0.8,5.9-0.7c0,0,1.4,0.7,1.5,1.6z"/>
+  </g>
+  <g id="g654" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path656" d="m-74.6,2.2s-8.52-2.791-27,0.6c0,0,9.031-2.078,27.8,0.2,10.3,1.25-0.8-0.8-0.8-0.8z"/>
+  </g>
+  <g id="g658" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path660" d="m-72.502,2.129s-8.246-3.518-26.951-1.737c0,0,9.178-1.289,27.679,2.603,10.154,2.136-0.728-0.866-0.728-0.866z"/>
+  </g>
+  <g id="g662" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path664" d="m-70.714,2.222s-7.962-4.121-26.747-3.736c0,0,9.248-0.604,27.409,4.654,9.966,2.885-0.662-0.918-0.662-0.918z"/>
+  </g>
+  <g id="g666" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path668" d="m-69.444,2.445s-6.824-4.307-23.698-5.405c0,0,8.339,0.17,24.22,6.279,8.716,3.353-0.522-0.874-0.522-0.874z"/>
+  </g>
+  <g id="g670" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path672" d="m45.84,12.961s-0.93,0.644-0.716-0.537c0.215-1.181,28.423-14.351,32.037-14.101,0,0-30.248,13.206-31.321,14.638z"/>
+  </g>
+  <g id="g674" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path676" d="m42.446,13.6s-0.876,0.715-0.755-0.479,27.208-16.539,30.83-16.573c0,0-29.117,15.541-30.075,17.052z"/>
+  </g>
+  <g id="g678" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path680" d="m39.16,14.975s-0.828,0.772-0.786-0.428c0.042-1.199,19.859-16.696,29.671-18.57,0,0-18.03,8.127-28.885,18.998z"/>
+  </g>
+  <g id="g682" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path684" d="m36.284,16.838s-0.745,0.694-0.707-0.385c0.038-1.08,17.872-15.027,26.703-16.713,0,0-16.226,7.314-25.996,17.098z"/>
+  </g>
+  <g id="g686" fill="#CCC">
+   <path id="path688" d="m4.6,164.8s-15.2-2.4,1.6-4c0,0,18-2,22-7.2,0,0,13.6-9.2,16.4-9.6s19.2-4,19.6-6.4,6.4-4.8,8-4,1.6,10-1.2,10.8-21.6,8-28,9.2-18,8.8-22.8,10-15.6,1.2-15.6,1.2z"/>
+  </g>
+  <g id="g690" fill="#000">
+   <path id="path692" d="m77.6,127.4s-3,1.6-4.2,4.2c0,0-6.4,10.6-20.6,13.8,0,0-23,9-30.8,11,0,0-13.4,5-20.8,4.2,0,0-7,0.2-0.8,1.8,0,0,20.2-2,23.6-3.8,0,0,15.6-5.2,18.6-7.8s21.2-7.6,23.4-9.6,12-10.4,11.6-13.8z"/>
+  </g>
+  <g id="g694" fill="#000">
+   <path id="path696" d="m18.882,158.91s5.229-0.23,4.076,1.32-3.601,0.68-3.601,0.68l-0.475-2z"/>
+  </g>
+  <g id="g698" fill="#000">
+   <path id="path700" d="m11.68,160.26s5.228-0.22,4.076,1.33c-1.153,1.55-3.601,0.67-3.601,0.67l-0.475-2z"/>
+  </g>
+  <g id="g702" fill="#000">
+   <path id="path704" d="m1.251,161.51s5.229-0.23,4.076,1.32-3.601,0.68-3.601,0.68l-0.475-2z"/>
+  </g>
+  <g id="g706" fill="#000">
+   <path id="path708" d="m-6.383,162.06s5.229-0.23,4.076,1.32-3.601,0.67-3.601,0.67l-0.475-1.99z"/>
+  </g>
+  <g id="g710" fill="#000">
+   <path id="path712" d="m35.415,151.51s6.96-0.3,5.425,1.76c-1.534,2.07-4.793,0.9-4.793,0.9l-0.632-2.66z"/>
+  </g>
+  <g id="g714" fill="#000">
+   <path id="path716" d="m45.73,147.09s5.959-3.3,5.425,1.76c-0.27,2.55-4.793,0.9-4.793,0.9l-0.632-2.66z"/>
+  </g>
+  <g id="g718" fill="#000">
+   <path id="path720" d="m54.862,144.27s7.159-3.7,5.425,1.77c-0.778,2.44-4.794,0.9-4.794,0.9l-0.631-2.67z"/>
+  </g>
+  <g id="g722" fill="#000">
+   <path id="path724" d="m64.376,139.45s4.359-4.9,5.425,1.76c0.406,2.54-4.793,0.9-4.793,0.9l-0.632-2.66z"/>
+  </g>
+  <g id="g726" fill="#000">
+   <path id="path728" d="m26.834,156s5.228-0.23,4.076,1.32c-1.153,1.55-3.602,0.68-3.602,0.68l-0.474-2z"/>
+  </g>
+  <g id="g730" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path732" d="m62.434,34.603s-0.726,0.665-0.727-0.406c0-1.07,17.484-14.334,26.327-15.718,0,0-16.099,6.729-25.6,16.124z"/>
+  </g>
+  <g id="g734" fill="#000">
+   <path id="path736" d="m65.4,98.4s22.001,22.4,31.201,26c0,0,9.199,11.2,5.199,37.2,0,0-3.199,7.6-6.399-13.2,0,0,3.2-25.2-8-9.2,0,0-8.401-9.9-2.001-9.6,0,0,3.201,2,3.601,0.4s-7.601-15.2-24.801-29.6,1.2-2,1.2-2z"/>
+  </g>
+  <g id="g738" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path740" d="m7,137.2s-0.2-1.8,1.6-1,96,7,127.6,31c0,0-45.199-23.2-129.2-30z"/>
+  </g>
+  <g id="g742" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path744" d="m17.4,132.8s-0.2-1.8,1.6-1,138.4-0.2,162,32.2c0,0-22-25.2-163.6-31.2z"/>
+  </g>
+  <g id="g746" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path748" d="m29,128.8s-0.2-1.8,1.6-1,175.2-12.2,198.8,20.2c0,0-9.6-25.6-200.4-19.2z"/>
+  </g>
+  <g id="g750" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path752" d="m39,124s-0.2-1.8,1.6-1,124-37.8,147.6-5.4c0,0-13.4-24.6-149.2,6.4z"/>
+  </g>
+  <g id="g754" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path756" d="m-19,146.8s-0.2-1.8,1.6-1,19.6,3,21.6,41.8c0,0-7.2-42-23.2-40.8z"/>
+  </g>
+  <g id="g758" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path760" d="m-27.8,148.4s-0.2-1.8,1.6-1,16-3.8,13.2,35c0,0,1.2-35.2-14.8-34z"/>
+  </g>
+  <g id="g762" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path764" d="m-35.8,148.8s-0.2-1.8,1.6-1,17.2,1.4,4.8,23.8c0,0,9.6-24-6.4-22.8z"/>
+  </g>
+  <g id="g766" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path768" d="m11.526,104.46s-0.444,2,1.105,0.79c16.068-12.628,48.51-71.53,104.2-77.164,0,0-38.312-12.11-105.3,76.374z"/>
+  </g>
+  <g id="g770" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path772" d="m22.726,102.66s-1.363-1.19,0.505-1.81c1.868-0.63,114.31-73.13,153.6-65.164,0,0-27.11-7.51-154.1,66.974z"/>
+  </g>
+  <g id="g774" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path776" d="m1.885,108.77s-0.509,1.6,1.202,0.62c8.975-5.12,12.59-62.331,56.167-63.586,0,0-32.411-14.714-57.369,62.966z"/>
+  </g>
+  <g id="g778" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path780" d="m-18.038,119.79s-1.077,1.29,0.876,1.03c10.246-1.33,31.651-42.598,76.09-37.519,0,0-31.966-14.346-76.966,36.489z"/>
+  </g>
+  <g id="g782" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path784" d="m-6.8,113.67s-0.811,1.47,1.058,0.84c9.799-3.27,22.883-47.885,67.471-51.432,0,0-34.126-7.943-68.529,50.592z"/>
+  </g>
+  <g id="g786" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path788" d="m-25.078,124.91s-0.873,1.04,0.709,0.84c8.299-1.08,25.637-34.51,61.633-30.396,0,0-25.893-11.62-62.342,29.556z"/>
+  </g>
+  <g id="g790" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path792" d="m-32.677,130.82s-1.005,1.05,0.586,0.93c4.168-0.31,34.806-33.39,53.274-17.89,0,0-12.015-18.721-53.86,16.96z"/>
+  </g>
+  <g id="g794" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path796" d="m36.855,98.898s-1.201-1.355,0.731-1.74c1.932-0.384,122.63-58.097,160.59-45.231,0,0-25.94-10.874-161.32,46.971z"/>
+  </g>
+  <g id="g798" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path800" d="m3.4,163.2s-0.2-1.8,1.6-1,17.2,1.4,4.8,23.8c0,0,9.6-24-6.4-22.8z"/>
+  </g>
+  <g id="g802" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path804" d="m13.8,161.6s-0.2-1.8,1.6-1,19.6,3,21.6,41.8c0,0-7.2-42-23.2-40.8z"/>
+  </g>
+  <g id="g806" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path808" d="m20.6,160s-0.2-1.8,1.6-1,26.4,4.2,50,36.6c0,0-35.6-36.8-51.6-35.6z"/>
+  </g>
+  <g id="g810" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path812" d="m28.225,157.97s-0.437-1.76,1.453-1.2c1.89,0.55,22.324-1.35,60.421,32.83,0,0-46.175-34.94-61.874-31.63z"/>
+  </g>
+  <g id="g814" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path816" d="m38.625,153.57s-0.437-1.76,1.453-1.2c1.89,0.55,36.724,5.05,88.422,40.03,0,0-74.176-42.14-89.875-38.83z"/>
+  </g>
+  <g id="g818" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path820" d="m-1.8,142s-0.2-1.8,1.6-1,55.2,3.4,85.6,30.2c0,0-34.901-24.77-87.2-29.2z"/>
+  </g>
+  <g id="g822" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path824" d="m-11.8,146s-0.2-1.8,1.6-1,26.4,4.2,50,36.6c0,0-35.6-36.8-51.6-35.6z"/>
+  </g>
+  <g id="g826" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path828" d="m49.503,148.96s-0.565-1.72,1.361-1.3c1.926,0.41,36.996,2.34,91.116,33.44,0,0-77.663-34.4-92.477-32.14z"/>
+  </g>
+  <g id="g830" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path832" d="m57.903,146.56s-0.565-1.72,1.361-1.3c1.926,0.41,36.996,2.34,91.116,33.44,0,0-77.063-34.8-92.477-32.14z"/>
+  </g>
+  <g id="g834" stroke-width="0.1" stroke="#000" fill="#FFF">
+   <path id="path836" d="m67.503,141.56s-0.565-1.72,1.361-1.3c1.926,0.41,44.996,4.74,134.72,39.04,0,0-120.66-40.4-136.08-37.74z"/>
+  </g>
+  <g id="g838" fill="#000">
+   <path id="path840" d="m-43.8,148.4s5.2-0.4,4,1.2-3.6,0.8-3.6,0.8l-0.4-2z"/>
+  </g>
+  <g id="g842" fill="#000">
+   <path id="path844" d="m-13,162.4s5.2-0.4,4,1.2-3.6,0.8-3.6,0.8l-0.4-2z"/>
+  </g>
+  <g id="g846" fill="#000">
+   <path id="path848" d="m-21.8,162s5.2-0.4,4,1.2-3.6,0.8-3.6,0.8l-0.4-2z"/>
+  </g>
+  <g id="g850" fill="#000">
+   <path id="path852" d="m-117.17,150.18s5.05,1.32,3.39,2.44-3.67-0.42-3.67-0.42l0.28-2.02z"/>
+  </g>
+  <g id="g854" fill="#000">
+   <path id="path856" d="m-115.17,140.58s5.05,1.32,3.39,2.44-3.67-0.42-3.67-0.42l0.28-2.02z"/>
+  </g>
+  <g id="g858" fill="#000">
+   <path id="path860" d="m-122.37,136.18s5.05,1.32,3.39,2.44-3.67-0.42-3.67-0.42l0.28-2.02z"/>
+  </g>
+  <g id="g862" fill="#CCC">
+   <path id="path864" d="m-42.6,211.2-5.6,2c-2,0-13.2,3.6-18.8,13.6,0,0,12.4-9.6,24.4-15.6z"/>
+  </g>
+  <g id="g866" fill="#CCC">
+   <path id="path868" d="m45.116,303.85c0.141,0.25,0.196,0.67,0.488,0.69,0.658,0.04,1.891,0.34,1.766-0.29-0.848-4.31-1.722-9.25-5.855-11.05-0.639-0.28-2.081,0.13-2.155,1.02-0.127,1.52-0.244,2.87,0.065,4.33,0.3,1.43,2.458,1.43,3.375,0.05,0.936,1.67,1.368,3.52,2.316,5.25z"/>
+  </g>
+  <g id="g870" fill="#CCC">
+   <path id="path872" d="m34.038,308.58c0.748,1.41,0.621,3.27,2.036,3.84,0.74,0.29,2.59-0.68,2.172-1.76-0.802-2.06-1.19-4.3-2.579-6.11-0.2-0.26,0.04-0.79-0.12-1.12-0.594-1.22-1.739-1.96-3.147-1.63-1.115,2.2,0.033,4.33,1.555,6.04,0.136,0.15-0.03,0.53,0.083,0.74z"/>
+  </g>
+  <g id="g874" fill="#CCC">
+   <path id="path876" d="m-5.564,303.39c-0.108-0.38-0.146-0.84,0.019-1.16,0.531-1.03,1.324-2.15,0.987-3.18-0.348-1.05-1.464-0.87-2.114-0.3-1.135,0.99-1.184,2.82-1.875,4.18-0.196,0.38-0.145,0.96-0.586,1.35-0.474,0.42-0.914,1.94-0.818,2.51,0.053,0.32-0.13,10.22,0.092,9.96,0.619-0.73,3.669-10.47,3.738-11.36,0.057-0.73,0.789-1.19,0.557-2z"/>
+  </g>
+  <g id="g878" fill="#CCC">
+   <path id="path880" d="m-31.202,296.6c2.634-2.5,5.424-5.46,4.982-9.17-0.116-0.98-1.891-0.45-2.078,0.39-0.802,3.63-2.841,6.29-5.409,8.68-2.196,2.05-4.058,8.39-4.293,8.9,3.697-5.26,5.954-8,6.798-8.8z"/>
+  </g>
+  <g id="g882" fill="#CCC">
+   <path id="path884" d="m-44.776,290.64c0.523-0.38,0.221-0.87,0.438-1.2,0.953-1.46,2.254-2.7,2.272-4.44,0.003-0.28-0.375-0.59-0.71-0.36-0.277,0.18-0.619,0.31-0.727,0.44-2.03,2.45-3.43,5.12-4.873,7.93-0.183,0.36-1.327,4.85-1.014,4.96,0.239,0.09,1.959-4.09,2.169-4.21,1.263-0.68,1.275-2.3,2.445-3.12z"/>
+  </g>
+  <g id="g886" fill="#CCC">
+   <path id="path888" d="m-28.043,310.18c0.444-0.87,2.02-2.07,1.907-2.96-0.118-0.93,0.35-2.37-0.562-1.68-1.257,0.94-4.706,2.29-4.976,8.1-0.026,0.57,2.948-2.12,3.631-3.46z"/>
+  </g>
+  <g id="g890" fill="#CCC">
+   <path id="path892" d="m-13.6,293c0.4-0.67,1.108-0.19,1.567-0.46,0.648-0.37,1.259-0.93,1.551-1.58,0.97-2.14,2.739-3.96,2.882-6.36-1.491-1.4-2.17,0.64-2.8,1.6-1.323-1.65-2.322,0.23-3.622,0.75-0.07,0.03-0.283-0.32-0.358-0.29-1.177,0.44-1.857,1.52-2.855,2.3-0.171,0.13-0.576-0.05-0.723,0.09-0.652,0.6-1.625,0.93-1.905,1.61-1.11,2.7-4.25,4.8-6.137,12.34,0.381,0.91,4.512-6.64,4.999-7.34,0.836-1.2,0.954,1.66,2.23,1,0.051-0.03,0.237,0.21,0.371,0.34,0.194-0.28,0.412-0.51,0.8-0.4,0-0.4-0.134-0.96,0.067-1.11,1.237-0.98,1.153-2.05,1.933-3.29,0.458,0.79,1.519,0.07,2,0.8z"/>
+  </g>
+  <g id="g894" fill="#CCC">
+   <path id="path896" d="m46.2,347.4s7.4-20.4,3-31.6c0,0,11.4,21.6,6.8,32.8,0,0-0.4-10.4-4.4-15.4,0,0-4,12.8-5.4,14.2z"/>
+  </g>
+  <g id="g898" fill="#CCC">
+   <path id="path900" d="m31.4,344.8s5.4-8.8-2.6-27.2c0,0-0.8,20.4-7.6,31.4,0,0,14.2-20.2,10.2-4.2z"/>
+  </g>
+  <g id="g902" fill="#CCC">
+   <path id="path904" d="m21.4,342.8s-0.2-20,0.2-23c0,0-3.8,16.6-14,26.2,0,0,14.4-12,13.8-3.2z"/>
+  </g>
+  <g id="g906" fill="#CCC">
+   <path id="path908" d="m11.8,310.8s6,13.6-4,32c0,0,6.4-12.2,1.6-19.2,0,0,2.6-3.4,2.4-12.8z"/>
+  </g>
+  <g id="g910" fill="#CCC">
+   <path id="path912" d="m-7.4,342.4s-1-15.6,0.8-17.8c0,0,0.2-6.4-0.2-7.4,0,0,4-6.2,4.2,1.2,0,0,1.4,7.8,4.2,12.4,0,0,3.6,5.4,3.4,11.8,0,0-10-30.2-12.4-0.2z"/>
+  </g>
+  <g id="g914" fill="#CCC">
+   <path id="path916" d="m-11,314.8s-6.6,10.8-8.4,29.8c0,0-1.4-6.2,2.4-20.6,0,0,4.2-15.4,6-9.2z"/>
+  </g>
+  <g id="g918" fill="#CCC">
+   <path id="path920" d="m-32.8,334.6s5-5.4,6.4-10.4c0,0,3.6-15.8-2.8-7.2,0,0,0.2,8-8,15.4,0,0,4.8-2.4,4.4,2.2z"/>
+  </g>
+  <g id="g922" fill="#CCC">
+   <path id="path924" d="m-38.6,329.6s3.4-17.4,4.2-18.2c0,0,1.8-3.4-1-0.2,0,0-8.8,19.2-12.8,25.8,0,0,8-9.2,9.6-7.4z"/>
+  </g>
+  <g id="g926" fill="#CCC">
+   <path id="path928" d="m-44.4,313s11.6-22.4-10.2,3.4c0,0,11-9.8,10.2-3.4z"/>
+  </g>
+  <g id="g930" fill="#CCC">
+   <path id="path932" d="m-59.8,298.4s4.8-18.8,7.4-18.6l1.6,1.6s-6,9.6-5.4,19.4c0,0-0.6-9.6-3.6-2.4z"/>
+  </g>
+  <g id="g934" fill="#CCC">
+   <path id="path936" d="m270.5,287s-12-10-14.5-13.5c0,0,13.5,18.5,13.5,25.5,0,0,2.5-7.5,1-12z"/>
+  </g>
+  <g id="g938" fill="#CCC">
+   <path id="path940" d="m276,265s-21-15-24.5-22.5c0,0,26.5,29.5,26.5,34,0,0,0.5-9-2-11.5z"/>
+  </g>
+  <g id="g942" fill="#CCC">
+   <path id="path944" d="m293,111s-12-8-13.5-6c0,0,10.5,6.5,13,15,0,0-1.5-9,0.5-9z"/>
+  </g>
+  <g id="g946" fill="#CCC">
+   <path id="path948" d="m301.5,191.5-17.5-12s19,17,19.5,21l-2-9z"/>
+  </g>
+  <g id="g950" stroke="#000">
+   <path id="path952" d="m-89.25,169,22,4.75"/>
+  </g>
+  <g id="g954" stroke="#000">
+   <path id="path956" d="m-39,331s-0.5-3.5-9.5,7"/>
+  </g>
+  <g id="g958" stroke="#000">
+   <path id="path960" d="m-33.5,336s2-6.5-4.5-2"/>
+  </g>
+  <g id="g962" stroke="#000">
+   <path id="path964" d="m20.5,344.5s1.5-11-10,2"/>
+  </g>
+ </g>
+</svg>
\ No newline at end of file
diff --git a/tests/fill.c b/tests/fill.c
new file mode 100644 (file)
index 0000000..0986a45
--- /dev/null
@@ -0,0 +1,29 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    vkvg_set_source_rgba(ctx,0.1,0.9,0.1,1.0);
+    vkvg_move_to(ctx,10,10);
+    vkvg_rel_line_to(ctx,100,100);
+    vkvg_rel_line_to(ctx,100,-100);
+    vkvg_rel_line_to(ctx,100,400);
+//    vkvg_line_to(ctx,400,350);
+//    vkvg_line_to(ctx,900,150);
+//    vkvg_line_to(ctx,700,450);
+//    vkvg_line_to(ctx,900,750);
+//    vkvg_line_to(ctx,500,650);
+//    vkvg_line_to(ctx,100,800);
+//    vkvg_line_to(ctx,150,400);
+    vkvg_close_path(ctx);
+    vkvg_fill(ctx);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/fill_and_stroke.c b/tests/fill_and_stroke.c
new file mode 100644 (file)
index 0000000..1174884
--- /dev/null
@@ -0,0 +1,31 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    vkvg_move_to (ctx, 100, 100);
+    vkvg_rel_line_to (ctx, 50, -80);
+    vkvg_rel_line_to (ctx, 50, 80);
+    //vkvg_close_path (ctx);
+
+    vkvg_move_to (ctx, 300, 100);
+    vkvg_rel_line_to (ctx, 50, -80);
+    vkvg_rel_line_to (ctx, 50, 80);
+    vkvg_close_path (ctx);
+
+    vkvg_set_line_width (ctx, 10.0);
+    vkvg_set_source_rgb (ctx, 0, 0, 1);
+    vkvg_fill_preserve (ctx);
+    //vkvg_fill(ctx);
+    vkvg_set_source_rgb (ctx, 0, 0, 0);
+    vkvg_stroke (ctx);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/gradient.c b/tests/gradient.c
new file mode 100644 (file)
index 0000000..b1a36cd
--- /dev/null
@@ -0,0 +1,25 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    VkvgPattern pat = vkvg_pattern_create_linear(100,0,300,0);
+    vkvg_set_line_width(ctx, 20);
+    vkvg_patter_add_color_stop(pat, 0, 1, 0, 0, 1);
+    vkvg_patter_add_color_stop(pat, 0.5, 0, 1, 0, 1);
+    vkvg_patter_add_color_stop(pat, 1, 0, 0, 1, 1);
+    vkvg_set_source (ctx, pat);
+    vkvg_rectangle(ctx,100,100,200,200);
+    vkvg_fill (ctx);
+    //vkvg_stroke (ctx);
+    vkvg_pattern_destroy (pat);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/gradient_transform.c b/tests/gradient_transform.c
new file mode 100644 (file)
index 0000000..7f6ef55
--- /dev/null
@@ -0,0 +1,30 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    //vkvg_translate(ctx,-100,-100);
+    //vkvg_rotate(ctx,1.5);
+    //vkvg_translate(ctx,100,100);
+
+    vkvg_scale(ctx,2,2);
+    VkvgPattern pat = vkvg_pattern_create_linear(0,0,200,0);
+    vkvg_set_line_width(ctx, 20);
+    vkvg_patter_add_color_stop(pat, 0, 1, 0, 0, 1);
+    vkvg_patter_add_color_stop(pat, 0.5, 0, 1, 0, 1);
+    vkvg_patter_add_color_stop(pat, 1, 0, 0, 1, 1);
+    vkvg_set_source (ctx, pat);
+    vkvg_rectangle(ctx,0,0,200,200);
+    vkvg_fill (ctx);
+    //vkvg_stroke (ctx);
+    vkvg_pattern_destroy (pat);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/img_surf.c b/tests/img_surf.c
new file mode 100644 (file)
index 0000000..be9bb05
--- /dev/null
@@ -0,0 +1,34 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    VkvgSurface imgSurf;// = vkvg_surface_create_from_image(device, "/mnt/data/images/blason.png");
+    //VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "/mnt/data/images/2000px-Tux.svg.png");
+    //VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "/mnt/data/images/path2674.png");
+    //VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "/mnt/data/images/horse-black-head-shape-of-a-chess-piece_318-52446.jpg");
+    /*vkvg_set_source_surface(ctx, imgSurf, 200, 200);
+    vkvg_paint(ctx);
+    vkvg_set_source_surface(ctx, imgSurf, 400, 400);
+    vkvg_paint(ctx);
+    vkvg_flush(ctx);
+    vkvg_surface_destroy(imgSurf);*/
+
+    imgSurf = vkvg_surface_create_from_image(device, "data/miroir.jpg");
+    fflush(stdout);
+    vkvg_set_source_surface(ctx, imgSurf, 0, 0);
+    vkvg_paint(ctx);
+    //vkvg_flush(ctx);
+    //vkvg_set_source_rgba(ctx,1,0,0,1);
+
+    vkvg_surface_destroy(imgSurf);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/line_caps.c b/tests/line_caps.c
new file mode 100644 (file)
index 0000000..c348d4a
--- /dev/null
@@ -0,0 +1,57 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    float x = 20, y = 20, dx = 30, dy = 60;
+
+    //vkvg_scale(ctx,5,5);
+    vkvg_set_line_width(ctx,26);
+    vkvg_set_source_rgba(ctx,0,0,0,1);
+    vkvg_move_to(ctx,x,y);
+    vkvg_rel_line_to(ctx,0,dy);
+    vkvg_stroke(ctx);
+    vkvg_set_line_cap(ctx,VKVG_LINE_CAP_SQUARE);
+    vkvg_move_to(ctx,x+dx,y);
+    vkvg_rel_line_to(ctx,0,dy);
+    vkvg_stroke(ctx);
+    vkvg_set_line_cap(ctx,VKVG_LINE_CAP_ROUND);
+    vkvg_move_to(ctx,x+2*dx,y);
+    vkvg_rel_line_to(ctx,0,dy);
+    vkvg_rel_move_to(ctx,dx,-dy);
+    vkvg_rel_line_to(ctx,dx,dy);
+    vkvg_rel_move_to(ctx,dx,-dy/2);
+    vkvg_rel_line_to(ctx,dx,0);
+    vkvg_rel_move_to(ctx,dx,dy/2);
+    vkvg_rel_line_to(ctx,dx,-dy);
+    vkvg_rel_move_to(ctx,dx,dy);
+    vkvg_rel_line_to(ctx,0,-dy);
+    vkvg_rel_move_to(ctx,2*dx,dy);
+    vkvg_rel_line_to(ctx,-dx,-dy);
+    vkvg_rel_move_to(ctx,3*dx,dy/2);
+    vkvg_rel_line_to(ctx,-dx,0);
+    //vkvg_rel_line_to(ctx,0,-dy);
+    //vkvg_rel_move_to(ctx,dx,dy/2);
+    //vkvg_rel_line_to(ctx,dx,0);
+    vkvg_stroke(ctx);
+
+    vkvg_set_line_cap(ctx,VKVG_LINE_CAP_BUTT);
+    vkvg_set_line_width(ctx,1);
+    vkvg_set_source_rgba(ctx,1,0,0,1);
+    vkvg_move_to(ctx,x,y);
+    vkvg_rel_line_to(ctx,0,dy);
+    vkvg_rel_move_to(ctx,dx,-dy);
+    vkvg_rel_line_to(ctx,0,dy);
+    vkvg_rel_move_to(ctx,dx,-dy);
+    vkvg_rel_line_to(ctx,0,dy);
+    vkvg_stroke(ctx);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/line_join.c b/tests/line_join.c
new file mode 100644 (file)
index 0000000..03c9d25
--- /dev/null
@@ -0,0 +1,74 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    float x = 50, y = 150, dx = 150, dy = 140;
+
+    vkvg_scale(ctx,2,2);
+
+    vkvg_set_line_width(ctx,40);
+    vkvg_set_source_rgba(ctx,0,0,0,1);
+
+
+    vkvg_set_line_join(ctx,VKVG_LINE_JOIN_ROUND);
+    //vkvg_rectangle(ctx,x,y,dx,dy);
+
+    vkvg_move_to(ctx,x,y);
+    vkvg_rel_line_to(ctx,50,-30);
+    vkvg_rel_line_to(ctx,50,0);
+    vkvg_rel_line_to(ctx,50,30);
+    vkvg_rel_line_to(ctx,0,60);
+    vkvg_rel_line_to(ctx,-50,70);
+    vkvg_rel_line_to(ctx,-50,0);
+    vkvg_rel_line_to(ctx,-50,-70);
+    vkvg_close_path(ctx);
+    vkvg_stroke(ctx);
+
+    vkvg_set_source_rgba(ctx,1,0,0,1);
+    vkvg_move_to(ctx,x+200,y);
+    vkvg_rel_line_to(ctx,50,70);
+    vkvg_rel_line_to(ctx,50,0);
+    vkvg_rel_line_to(ctx,50,-70);
+    vkvg_rel_line_to(ctx,0,-60);
+    vkvg_rel_line_to(ctx,-50,-30);
+    vkvg_rel_line_to(ctx,-50,0);
+    vkvg_rel_line_to(ctx,-50,30);
+    vkvg_close_path(ctx);
+    vkvg_stroke(ctx);
+
+    vkvg_move_to(ctx,x,y);
+    vkvg_rel_line_to(ctx,50,-30);
+    vkvg_rel_line_to(ctx,50,0);
+    vkvg_rel_line_to(ctx,50,30);
+    vkvg_rel_line_to(ctx,0,60);
+    vkvg_rel_line_to(ctx,-50,70);
+    vkvg_rel_line_to(ctx,-50,0);
+    vkvg_rel_line_to(ctx,-50,-70);
+    vkvg_close_path(ctx);
+    vkvg_stroke(ctx);
+
+//    vkvg_rel_line_to(ctx,dx,-dy);
+//    vkvg_rel_line_to(ctx,dx,dy);
+//    vkvg_stroke(ctx);
+//    vkvg_set_line_join(ctx,VKVG_LINE_JOIN_BEVEL);
+//    vkvg_rel_move_to(ctx,-dx*2,abs(dy*1.5));
+//    vkvg_rel_line_to(ctx,dx,-dy);
+//    vkvg_rel_line_to(ctx,dx,dy);
+//    vkvg_stroke(ctx);
+//    vkvg_set_line_join(ctx,VKVG_LINE_JOIN_ROUND);
+//    vkvg_rel_move_to(ctx,-dx*2,abs(dy*1.5));
+//    vkvg_rel_line_to(ctx,dx,-dy);
+//    vkvg_rel_line_to(ctx,dx,dy);
+//    vkvg_stroke(ctx);
+//    vkvg_set_line_join(ctx,VKVG_LINE_JOIN_MITER);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/multi.c b/tests/multi.c
new file mode 100644 (file)
index 0000000..be0f55a
--- /dev/null
@@ -0,0 +1,14 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/painting.c b/tests/painting.c
new file mode 100644 (file)
index 0000000..8b8e225
--- /dev/null
@@ -0,0 +1,40 @@
+#include "test.h"
+
+void test(){
+
+    VkvgSurface surf2 = vkvg_surface_create (device,400,400);;
+    VkvgContext ctx = vkvg_create (surf2);
+
+    vkvg_set_source_rgba(ctx,1.0,0.,0.,1.0);
+    vkvg_paint (ctx);
+
+    vkvg_destroy (ctx);
+    ctx = vkvg_create (surf);
+
+    vkvg_set_source_rgba(ctx,0.1,0.1,0.3,1.0);
+    vkvg_paint (ctx);
+
+    //vkvg_set_source_surface(ctx,surf2,0,0);
+
+    //VkvgPattern pat = vkvg_get_source (ctx);
+    VkvgPattern pat = vkvg_pattern_create_for_surface(surf2);
+    vkvg_pattern_set_extend (pat,VKVG_EXTEND_REFLECT);
+    vkvg_set_source(ctx,pat);
+    //vkvg_paint (ctx);
+    //vkvg_set_source_rgba(ctx,0,1,0,1.0);
+    vkvg_rectangle(ctx,100,100,200,200);
+    vkvg_fill(ctx);
+
+    vkvg_destroy (ctx);
+    vkvg_surface_destroy (surf2);
+    vkvg_pattern_destroy (pat);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/random_rects.c b/tests/random_rects.c
new file mode 100644 (file)
index 0000000..6c3edd0
--- /dev/null
@@ -0,0 +1,33 @@
+#include "test.h"
+
+void test(){
+    vkvg_surface_clear(surf);
+    struct timeval currentTime;
+    gettimeofday(&currentTime, NULL);
+
+    srand((unsigned) currentTime.tv_usec);
+    const float w = 1024.f;
+
+    VkvgContext ctx = vkvg_create(surf);
+    vkvg_set_line_width(ctx,1);
+    for (int i=0; i<5000; i++) {
+        randomize_color(ctx);
+        float x = trunc( (0.5*(float)w*rand())/RAND_MAX );
+        float y = trunc( (0.5*(float)w*rand())/RAND_MAX );
+        float z = trunc( (0.5*(float)w*rand())/RAND_MAX ) + 1;
+        float v = trunc( (0.5*(float)w*rand())/RAND_MAX ) + 1;
+
+        vkvg_rectangle(ctx, x+1, y+1, z, v);
+        vkvg_fill_preserve(ctx);
+        randomize_color(ctx);
+        vkvg_stroke(ctx);
+    }
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/rect_fill.c b/tests/rect_fill.c
new file mode 100644 (file)
index 0000000..9453a3c
--- /dev/null
@@ -0,0 +1,23 @@
+#include "test.h"
+
+void test(){
+    vkvg_surface_clear(surf);
+    VkvgContext ctx = vkvg_create(surf);
+
+    vkvg_set_line_width(ctx,10);
+    vkvg_set_source_rgba(ctx,0,0,1,0.5);
+    vkvg_rectangle(ctx,100,100,200,200);
+    vkvg_fill(ctx);
+    vkvg_rectangle(ctx,200,200,200,200);
+    vkvg_set_source_rgba(ctx,1,0,0,0.5);
+    vkvg_fill(ctx);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/simple_paint.c b/tests/simple_paint.c
new file mode 100644 (file)
index 0000000..f3c1161
--- /dev/null
@@ -0,0 +1,15 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+    vkvg_set_source_rgba(ctx,1,0,0,1);
+    vkvg_paint(ctx);
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/stroke.c b/tests/stroke.c
new file mode 100644 (file)
index 0000000..3f6d1d5
--- /dev/null
@@ -0,0 +1,52 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    vkvg_set_line_width(ctx, 2);
+    vkvg_set_source_rgba(ctx,1,0,0,1);
+    vkvg_move_to(ctx,200.5,200.5);
+    vkvg_line_to(ctx,400.5,200.5);
+    vkvg_line_to(ctx,400.5,400.5);
+    vkvg_line_to(ctx,200.5,400.5);
+    vkvg_close_path(ctx);
+    vkvg_stroke_preserve(ctx);
+    vkvg_set_source_rgba(ctx,0,0.2,0.35,1);
+    vkvg_fill(ctx);
+    vkvg_set_source_rgba(ctx,0.5,1,0,1);
+    vkvg_move_to(ctx,300.5,300.5);
+    vkvg_line_to(ctx,500.5,300.5);
+    vkvg_line_to(ctx,500.5,500.5);
+    vkvg_line_to(ctx,300.5,500.5);
+    vkvg_close_path(ctx);
+    vkvg_stroke(ctx);
+    vkvg_set_line_width(ctx, 40);
+    vkvg_set_source_rgba(ctx,0.5,0.6,1,1.0);
+    vkvg_move_to(ctx,700,475);
+    vkvg_line_to(ctx,400,475);
+    vkvg_stroke(ctx);
+    vkvg_set_source_rgba(ctx,0,0.5,0.5,0.5);
+    vkvg_move_to(ctx,300,200);
+    vkvg_arc(ctx, 200,200,100,0, M_PI);
+    vkvg_stroke(ctx);
+
+    vkvg_set_line_width(ctx, 20);
+    vkvg_set_source_rgba(ctx,0.1,0.1,0.1,0.5);
+    vkvg_move_to(ctx,100,60);
+    vkvg_line_to(ctx,400,600);
+    vkvg_stroke(ctx);
+
+    vkvg_set_source_rgba(ctx,1,1,1,1);
+    vkvg_set_line_width(ctx, 1);
+    vkvg_rectangle(ctx,600.5,200.5,100,60);
+    vkvg_stroke(ctx);
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/svg.c b/tests/svg.c
new file mode 100644 (file)
index 0000000..3dbec1b
--- /dev/null
@@ -0,0 +1,103 @@
+#include "test.h"
+
+#include "string.h" //for nanosvg
+#define NANOSVG_IMPLEMENTATION // Expands implementation
+#include "nanosvg.h"
+
+void svg_set_color (VkvgContext ctx, uint32_t c, float alpha) {
+    float a = (c >> 24 & 255) / 255.f;
+    float b = (c >> 16 & 255) / 255.f;
+    float g = (c >> 8 & 255) / 255.f;
+    float r = (c & 255) / 255.f;
+    vkvg_set_source_rgba(ctx,r,g,b,a*alpha);
+}
+
+static float rotation = 0.f;
+
+void test_svg () {
+    rotation+=0.01f;
+
+    vkvg_matrix_t mat;
+    vkvg_matrix_init_translate (&mat, 512,400);
+    vkvg_matrix_rotate(&mat,rotation);
+    vkvg_matrix_translate(&mat,-512,-400);
+
+    VkvgContext ctx = vkvg_create(surf);
+    vkvg_set_source_rgba(ctx,1.0,1.0,1.0,1);
+    vkvg_paint(ctx);
+
+    vkvg_set_matrix(ctx,&mat);
+
+    NSVGimage* svg;
+    NSVGshape* shape;
+    NSVGpath* path;
+    //svg = nsvgParseFromFile("/mnt/data/images/svg/tux.svg", "px", 96);
+    //svg = nsvgParseFromFile("/mnt/data/images/svg/world.svg", "px", 96);
+    svg = nsvgParseFromFile("data/tiger.svg", "px", 96);
+    //svg = nsvgParseFromFile("/mnt/data/images/svg/koch_curve.svg", "px", 96);
+    //svg = nsvgParseFromFile("/mnt/data/images/svg/diamond1.svg", "px", 96);
+    //svg = nsvgParseFromFile("/mnt/data/images/svg/diamond2.svg", "px", 96);
+    //svg = nsvgParseFromFile("/home/jp/yahweh-protosinaitic.svg", "px", 96);
+    //svg = nsvgParseFromFile("/mnt/data/images/svg/WMD-biological.svg", "px", 96);
+    //svg = nsvgParseFromFile("/mnt/data/images/svg/Skull_and_crossbones.svg", "px", 96);
+    //svg = nsvgParseFromFile("/mnt/data/images/svg/IconAlerte.svg", "px", 96);
+    //svg = nsvgParseFromFile("/mnt/data/images/svg/Svg_example4.svg", "px", 96);
+
+    //vkvg_scale(ctx, 3,3);
+    vkvg_set_source_rgba(ctx,0.0,0.0,0.0,1);
+
+    for (shape = svg->shapes; shape != NULL; shape = shape->next) {
+
+        vkvg_new_path(ctx);
+
+        float o = shape->opacity;
+
+        vkvg_set_line_width(ctx, shape->strokeWidth);
+
+        for (path = shape->paths; path != NULL; path = path->next) {
+            float* p = path->pts;
+            vkvg_move_to(ctx, p[0],p[1]);
+            for (int i = 1; i < path->npts-2; i += 3) {
+                p = &path->pts[i*2];
+                vkvg_curve_to(ctx, p[0],p[1], p[2],p[3], p[4],p[5]);
+            }
+            if (path->closed)
+                vkvg_close_path(ctx);
+        }
+
+        if (shape->fill.type == NSVG_PAINT_COLOR)
+            svg_set_color(ctx, shape->fill.color, o);
+        else if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT){
+            NSVGgradient* g = shape->fill.gradient;
+            svg_set_color(ctx, g->stops[0].color, o);
+        }
+
+        if (shape->fill.type != NSVG_PAINT_NONE){
+            if (shape->stroke.type == NSVG_PAINT_NONE){
+                vkvg_fill(ctx);
+                continue;
+            }
+            vkvg_fill_preserve (ctx);
+        }
+
+        if (shape->stroke.type == NSVG_PAINT_COLOR)
+            svg_set_color(ctx, shape->stroke.color, o);
+        else if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT){
+            NSVGgradient* g = shape->stroke.gradient;
+            svg_set_color(ctx, g->stops[0].color, o);
+        }
+
+        vkvg_stroke(ctx);
+    }
+
+    nsvgDelete(svg);
+
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test_svg, 1024, 768);
+    return 0;
+}
index 82aa6947708ecc3e60e08074d0fc3988c1a9c98c..d7235cb5c06343d0062bc415695d51967463fbff 100644 (file)
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
-#include "vkengine.h"
-#include "stdio.h"
-#include "stdlib.h"
-#include "time.h"
-#include "vkvg.h"
 
-#include "string.h" //for nanosvg
-#define NANOSVG_IMPLEMENTATION // Expands implementation
-#include "nanosvg.h"
-#include "vkh_device.h"
-#include "vkh_presenter.h"
-
-#ifdef _WIN32 // MSC_VER
-    #define WIN32_LEAN_AND_MEAN
-    #define NOMINMAX
-    #include <Windows.h> // Windows.h -> WinDef.h defines min() max()
-
-    /*
-        typedef uint16_t WORD ;
-        typedef uint32_t DWORD;
-
-        typedef struct _FILETIME {
-            DWORD dwLowDateTime;
-            DWORD dwHighDateTime;
-        } FILETIME;
-
-        typedef struct _SYSTEMTIME {
-              WORD wYear;
-              WORD wMonth;
-              WORD wDayOfWeek;
-              WORD wDay;
-              WORD wHour;
-              WORD wMinute;
-              WORD wSecond;
-              WORD wMilliseconds;
-        } SYSTEMTIME, *PSYSTEMTIME;
-    */
-
-    // *sigh* Microsoft has this in winsock2.h because they are too lazy to put it in the standard location ... !?!?
-    typedef struct timeval {
-        long tv_sec;
-        long tv_usec;
-    } timeval;
-
-    // *sigh* no gettimeofday on Win32/Win64
-    int gettimeofday(struct timeval * tp, struct timezone * tzp)
-    {
-        // FILETIME Jan 1 1970 00:00:00
-        // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
-        static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL);
-
-        SYSTEMTIME  nSystemTime;
-        FILETIME    nFileTime;
-        uint64_t    nTime;
-
-        GetSystemTime( &nSystemTime );
-        SystemTimeToFileTime( &nSystemTime, &nFileTime );
-        nTime =  ((uint64_t)nFileTime.dwLowDateTime )      ;
-        nTime += ((uint64_t)nFileTime.dwHighDateTime) << 32;
-
-        tp->tv_sec  = (long) ((nTime - EPOCH) / 10000000L);
-        tp->tv_usec = (long) (nSystemTime.wMilliseconds * 1000);
-        return 0;
-    }
-#else
-    #include <sys/time.h>
-#endif // _WIN32
-
-VkvgDevice device;
-VkvgSurface surf = NULL;
-
-static float panX = 0.f;
-static float panY = 0.f;
-static float lastX = 0.f;
-static float lastY = 0.f;
-static float zoom = 1.0f;
-static bool mouseDown = false;
-
-static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
-    if (action != GLFW_PRESS)
-        return;
-    switch (key) {
-    case GLFW_KEY_ESCAPE :
-        glfwSetWindowShouldClose(window, GLFW_TRUE);
-        break;
-    }
-}
-static void char_callback (GLFWwindow* window, uint32_t c){}
-static void mouse_move_callback(GLFWwindow* window, double x, double y){
-    if (mouseDown) {
-        panX += ((float)x-lastX);
-        panY += ((float)y-lastY);
-    }
-    lastX = (float)x;
-    lastY = (float)y;
-}
-static void scroll_callback(GLFWwindow* window, double x, double y){
-    if (y<0.f)
-        zoom *= 0.5f;
-    else
-        zoom *= 2.0f;
-}
-static void mouse_button_callback(GLFWwindow* window, int but, int state, int modif){
-    if (but != GLFW_MOUSE_BUTTON_1)
-        return;
-    if (state == GLFW_TRUE)
-        mouseDown = true;
-    else
-        mouseDown = false;
-}
-
-double time_diff(struct timeval x , struct timeval y)
-{
-    double x_ms , y_ms , diff;
-
-    x_ms = (double)x.tv_sec*1000000 + (double)x.tv_usec;
-    y_ms = (double)y.tv_sec*1000000 + (double)y.tv_usec;
-
-    diff = (double)y_ms - (double)x_ms;
-
-    return diff;
-}
-
-void randomize_color (VkvgContext ctx) {
-    vkvg_set_source_rgba(ctx,
-        (float)rand()/RAND_MAX,
-        (float)rand()/RAND_MAX,
-        (float)rand()/RAND_MAX,
-        (float)rand()/RAND_MAX
-    );
-}
-void vkvg_test_gradient (VkvgContext ctx) {
-    VkvgPattern pat = vkvg_pattern_create_linear(100,0,300,0);
-    vkvg_set_line_width(ctx, 20);
-    vkvg_patter_add_color_stop(pat, 0, 1, 0, 0, 1);
-    vkvg_patter_add_color_stop(pat, 0.5, 0, 1, 0, 1);
-    vkvg_patter_add_color_stop(pat, 1, 0, 0, 1, 1);
-    vkvg_set_source (ctx, pat);
-    vkvg_rectangle(ctx,100,100,200,200);
-    vkvg_fill (ctx);
-    //vkvg_stroke (ctx);
-    vkvg_pattern_destroy (pat);
-}
-
-void vkvg_test_clip(VkvgContext ctx){
-    vkvg_move_to(ctx,10,10);
-    vkvg_line_to(ctx,400,150);
-    vkvg_line_to(ctx,900,10);
-    vkvg_line_to(ctx,700,450);
-    vkvg_line_to(ctx,900,750);
-    vkvg_line_to(ctx,500,650);
-    vkvg_line_to(ctx,100,800);
-    vkvg_line_to(ctx,150,400);
-    vkvg_clip(ctx);
-}
-void vkvg_test_fill(VkvgContext ctx){
-    vkvg_set_source_rgba(ctx,0.1,0.9,0.1,1.0);
-    vkvg_move_to(ctx,10,10);
-    vkvg_rel_line_to(ctx,100,100);
-    vkvg_rel_line_to(ctx,100,-100);
-    vkvg_rel_line_to(ctx,100,400);
-//    vkvg_line_to(ctx,400,350);
-//    vkvg_line_to(ctx,900,150);
-//    vkvg_line_to(ctx,700,450);
-//    vkvg_line_to(ctx,900,750);
-//    vkvg_line_to(ctx,500,650);
-//    vkvg_line_to(ctx,100,800);
-//    vkvg_line_to(ctx,150,400);
-    vkvg_close_path(ctx);
-    vkvg_fill(ctx);
-}
-void vkvg_test_fill_and_stroke (VkvgContext ctx){
-    vkvg_move_to (ctx, 100, 100);
-    vkvg_rel_line_to (ctx, 50, -80);
-    vkvg_rel_line_to (ctx, 50, 80);
-    //vkvg_close_path (ctx);
-
-    vkvg_move_to (ctx, 300, 100);
-    vkvg_rel_line_to (ctx, 50, -80);
-    vkvg_rel_line_to (ctx, 50, 80);
-    vkvg_close_path (ctx);
-
-    vkvg_set_line_width (ctx, 10.0);
-    vkvg_set_source_rgb (ctx, 0, 0, 1);
-    vkvg_fill_preserve (ctx);
-    //vkvg_fill(ctx);
-    vkvg_set_source_rgb (ctx, 0, 0, 0);
-    vkvg_stroke (ctx);
-}
-void vkvg_test_curves2 (VkvgContext ctx) {
-
-    vkvg_move_to    (ctx, 100, 400);
-    vkvg_curve_to   (ctx, 100, 100, 600,700,600,400);
-    vkvg_curve_to   (ctx, 1000, 100, 100, 800, 1000, 800);
-    vkvg_curve_to   (ctx, 1000, 500, 700, 500, 700, 100);
-    vkvg_close_path(ctx);
-
-    //vkvg_set_source_rgba   (ctx, 0.5,0.0,1.0,0.5);
-    //vkvg_fill_preserve(ctx);
-
-    vkvg_set_source_rgba   (ctx, 0,0,0,1);
-    vkvg_set_line_width(ctx, 40);
-    vkvg_stroke(ctx);
-}
-void vkvg_test_curves (VkvgContext ctx){
-
-    vkvg_set_line_width(ctx, 10);
-    vkvg_set_source_rgb   (ctx, 0,0,0);
-
-    vkvg_arc(ctx, 150, 100, 10, 0, M_PI*2);
-    vkvg_fill(ctx);
-    vkvg_arc(ctx, 200, 200, 10, 0, M_PI*2);
-    vkvg_fill(ctx);
-
-    vkvg_set_source_rgba   (ctx, 0.5,0.0,1.0,0.5);
-    vkvg_move_to(ctx,100,100);
-    vkvg_line_to(ctx,200,100);
-    vkvg_curve_to(ctx,250,100,300,150,300,200);
-    vkvg_line_to(ctx,300,300);
-    vkvg_curve_to(ctx,300,350,250,400,200,400);
-    vkvg_line_to(ctx,100,400);
-    vkvg_curve_to(ctx,50,400,10,350,10,300);
-    vkvg_line_to(ctx,10,200);
-    vkvg_curve_to(ctx,10,150,50,100,100,100);
-    vkvg_close_path(ctx);
-    //vkvg_curve_to(ctx, 150,100,200,150,200,200);
-    vkvg_fill_preserve(ctx);
-    vkvg_set_source_rgba   (ctx, 1,1,1.0,0.5);
-    vkvg_stroke(ctx);
-}
-void vkvg_test_stroke(VkvgContext ctx){
-    vkvg_set_line_width(ctx, 2);
-    vkvg_set_source_rgba(ctx,1,0,0,1);
-    vkvg_move_to(ctx,200.5,200.5);
-    vkvg_line_to(ctx,400.5,200.5);
-    vkvg_line_to(ctx,400.5,400.5);
-    vkvg_line_to(ctx,200.5,400.5);
-    vkvg_close_path(ctx);
-    vkvg_stroke_preserve(ctx);
-    vkvg_set_source_rgba(ctx,0,0.2,0.35,1);
-    vkvg_fill(ctx);
-    vkvg_set_source_rgba(ctx,0.5,1,0,1);
-    vkvg_move_to(ctx,300.5,300.5);
-    vkvg_line_to(ctx,500.5,300.5);
-    vkvg_line_to(ctx,500.5,500.5);
-    vkvg_line_to(ctx,300.5,500.5);
-    vkvg_close_path(ctx);
-    vkvg_stroke(ctx);
-    vkvg_set_line_width(ctx, 40);
-    vkvg_set_source_rgba(ctx,0.5,0.6,1,1.0);
-    vkvg_move_to(ctx,700,475);
-    vkvg_line_to(ctx,400,475);
-    vkvg_stroke(ctx);
-    vkvg_set_source_rgba(ctx,0,0.5,0.5,0.5);
-    vkvg_move_to(ctx,300,200);
-    vkvg_arc(ctx, 200,200,100,0, M_PI);
-    vkvg_stroke(ctx);
-
-    vkvg_set_line_width(ctx, 20);
-    vkvg_set_source_rgba(ctx,0.1,0.1,0.1,0.5);
-    vkvg_move_to(ctx,100,60);
-    vkvg_line_to(ctx,400,600);
-    vkvg_stroke(ctx);
-}
-void test_text (VkvgContext ctx) {
-    int size = 19;
-    int penY = 50;
-    int penX = 10;
-
-    /*vkvg_rectangle(ctx,30,0,100,400);
-    vkvg_clip(ctx);*/
-
-    //vkvg_select_font_face(ctx, "/usr/local/share/fonts/DroidSansMono.ttf");
-    //vkvg_select_font_face(ctx, "/usr/share/fonts/truetype/unifont/unifont.ttf");
-
-    vkvg_set_font_size(ctx,12);
-    vkvg_select_font_face(ctx, "droid");
-    vkvg_font_extents_t fe;
-    vkvg_font_extents (ctx,&fe);
-    vkvg_move_to(ctx, penX,penY);
-    vkvg_set_source_rgba(ctx,0.7,0.7,0.7,1);
-    vkvg_text_extents_t te;
-    vkvg_text_extents(ctx,"abcdefghijk",&te);
-    vkvg_show_text (ctx,"abcdefghijk");
-    penX+= te.x_advance;
-    vkvg_move_to(ctx, penX,penY);
-    vkvg_show_text (ctx,"*abcdefghijk2");
-    penY+=2*size;
-
-
-    vkvg_select_font_face(ctx, "times");
-    vkvg_set_source_rgba(ctx,0.9,0.7,0.7,1);
-    vkvg_move_to(ctx, penX,penY);
-    vkvg_show_text (ctx,"abcdefghijklmnopqrstuvwxyz");
-    penY+=size;
-
-
-
-    vkvg_select_font_face(ctx, "droid");
-    vkvg_move_to(ctx, penX,penY);
-    vkvg_show_text (ctx,"lmnopqrstuvwxyz123456789");
-    penY+=size;
-
-
-
-    vkvg_select_font_face(ctx, "times:bold");
-    vkvg_move_to(ctx, penX,penY);
-    vkvg_show_text (ctx,"abcdefghijklmnopqrstuvwxyz");
-    penY+=size;
-
-
-    vkvg_select_font_face(ctx, "droid");
-    vkvg_move_to(ctx, penX,penY);
-    vkvg_show_text (ctx,"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-    penY+=size;
-
-
-
-    vkvg_select_font_face(ctx, "arial:italic");
-    vkvg_move_to(ctx, penX,penY);
-    vkvg_show_text (ctx,"abcdefghijklmnopqrstuvwxyz");
-    penY+=size;
-
-
-    vkvg_select_font_face(ctx, "arial");
-    vkvg_move_to(ctx, penX,penY);
-    vkvg_show_text (ctx,"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-    penY+=size;
-    vkvg_move_to(ctx, penX,penY);
-    vkvg_show_text (ctx,"this is a test");
-    penY+=size;
-    vkvg_move_to(ctx, penX,penY);
-    vkvg_show_text (ctx,"this is another test to see if label is working");
-    penY+=size;
-
-    vkvg_select_font_face(ctx, "mono");
-    vkvg_move_to(ctx, penX,penY);
-    vkvg_show_text (ctx,"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-    penY+=size;
-
-    vkvg_move_to(ctx, 80,400);
-    vkvg_show_text (ctx,"Ленивый рыжий кот");
-
-
-    /*vkvg_move_to(ctx, 150,250);
-    vkvg_show_text (ctx,"test string é€");
-    vkvg_move_to(ctx, 150,300);
-    vkvg_show_text (ctx,"كسول الزنجبيل القط");
-    vkvg_move_to(ctx, 150,350);
-    vkvg_show_text (ctx,"懶惰的姜貓");*/
-
-    //vkvg_show_text (ctx,"ABCDABCD");
-    //vkvg_show_text (ctx,"j");
-}
-void vkvg_test_stroke2(VkvgContext ctx){
-    vkvg_set_line_width(ctx,20);
-    vkvg_set_source_rgba(ctx,1,0,0,1);
-    vkvg_move_to(ctx,200,200);
-    vkvg_line_to(ctx,400,200);
-    vkvg_line_to(ctx,400,400);
-    vkvg_line_to(ctx,200,400);
-    vkvg_close_path(ctx);
-    vkvg_stroke(ctx);
-    vkvg_set_source_rgba(ctx,0.5,1,0,1);
-    vkvg_move_to(ctx,300,300);
-    vkvg_line_to(ctx,500,300);
-    vkvg_line_to(ctx,500,500);
-    vkvg_line_to(ctx,300,500);
-    vkvg_close_path(ctx);
-    vkvg_stroke(ctx);
-    vkvg_set_line_width(ctx,10);
-    vkvg_set_source_rgba(ctx,0.5,0.6,1,1);
-    vkvg_move_to(ctx,700,475);
-    vkvg_line_to(ctx,400,475);
-    vkvg_stroke(ctx);
-    vkvg_set_source_rgba(ctx,1,0,1,1);
-    vkvg_move_to(ctx,700,500);
-
-    vkvg_arc(ctx, 600,500,100,M_PI, 2.0*M_PI);
-    vkvg_stroke(ctx);
-
-
-    vkvg_set_line_width(ctx,20);
-    vkvg_set_source_rgba(ctx,1,1,0,1);
-    vkvg_move_to(ctx,100,50);
-    vkvg_line_to(ctx,400,50);
-    vkvg_stroke(ctx);
-}
-void vkvg_test_fill2(VkvgContext ctx){
-    vkvg_set_source_rgba(ctx,1,0,0,1);
-    vkvg_move_to(ctx,200,200);
-    vkvg_line_to(ctx,250,150);
-    vkvg_line_to(ctx,200,100);
-    vkvg_line_to(ctx,300,150);
-    vkvg_line_to(ctx,700,100);
-    vkvg_line_to(ctx,400,200);
-    vkvg_line_to(ctx,400,400);
-    vkvg_line_to(ctx,200,400);
-    vkvg_line_to(ctx,300,300);
-    vkvg_close_path(ctx);
-    vkvg_fill(ctx);
-}
-void test_img_surface () {
-    VkvgContext ctx = vkvg_create(surf);
-    VkvgSurface imgSurf;// = vkvg_surface_create_from_image(device, "/mnt/data/images/blason.png");
-    //VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "/mnt/data/images/2000px-Tux.svg.png");
-    //VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "/mnt/data/images/path2674.png");
-    //VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "/mnt/data/images/horse-black-head-shape-of-a-chess-piece_318-52446.jpg");
-    /*vkvg_set_source_surface(ctx, imgSurf, 200, 200);
-    vkvg_paint(ctx);
-    vkvg_set_source_surface(ctx, imgSurf, 400, 400);
-    vkvg_paint(ctx);
-    vkvg_flush(ctx);
-    vkvg_surface_destroy(imgSurf);*/
-
-    imgSurf = vkvg_surface_create_from_image(device, "/mnt/data/images/miroir.jpg");
-    fflush(stdout);
-    vkvg_set_source_surface(ctx, imgSurf, 0, 0);
-    vkvg_paint(ctx);
-    //vkvg_flush(ctx);
-    //vkvg_set_source_rgba(ctx,1,0,0,1);
-
-    vkvg_surface_destroy(imgSurf);
-    vkvg_destroy(ctx);
-}
-void test_line_caps (VkvgContext ctx) {
-
-    float x = 20, y = 20, dx = 30, dy = 60;
-
-    //vkvg_scale(ctx,5,5);
-    vkvg_set_line_width(ctx,26);
-    vkvg_set_source_rgba(ctx,0,0,0,1);
-    vkvg_move_to(ctx,x,y);
-    vkvg_rel_line_to(ctx,0,dy);
-    vkvg_stroke(ctx);
-    vkvg_set_line_cap(ctx,VKVG_LINE_CAP_SQUARE);
-    vkvg_move_to(ctx,x+dx,y);
-    vkvg_rel_line_to(ctx,0,dy);
-    vkvg_stroke(ctx);
-    vkvg_set_line_cap(ctx,VKVG_LINE_CAP_ROUND);
-    vkvg_move_to(ctx,x+2*dx,y);
-    vkvg_rel_line_to(ctx,0,dy);
-    vkvg_rel_move_to(ctx,dx,-dy);
-    vkvg_rel_line_to(ctx,dx,dy);
-    vkvg_rel_move_to(ctx,dx,-dy/2);
-    vkvg_rel_line_to(ctx,dx,0);
-    vkvg_rel_move_to(ctx,dx,dy/2);
-    vkvg_rel_line_to(ctx,dx,-dy);
-    vkvg_rel_move_to(ctx,dx,dy);
-    vkvg_rel_line_to(ctx,0,-dy);
-    vkvg_rel_move_to(ctx,2*dx,dy);
-    vkvg_rel_line_to(ctx,-dx,-dy);
-    vkvg_rel_move_to(ctx,3*dx,dy/2);
-    vkvg_rel_line_to(ctx,-dx,0);
-    //vkvg_rel_line_to(ctx,0,-dy);
-    //vkvg_rel_move_to(ctx,dx,dy/2);
-    //vkvg_rel_line_to(ctx,dx,0);
-    vkvg_stroke(ctx);
-
-    vkvg_set_line_cap(ctx,VKVG_LINE_CAP_BUTT);
-    vkvg_set_line_width(ctx,1);
-    vkvg_set_source_rgba(ctx,1,0,0,1);
-    vkvg_move_to(ctx,x,y);
-    vkvg_rel_line_to(ctx,0,dy);
-    vkvg_rel_move_to(ctx,dx,-dy);
-    vkvg_rel_line_to(ctx,0,dy);
-    vkvg_rel_move_to(ctx,dx,-dy);
-    vkvg_rel_line_to(ctx,0,dy);
-    vkvg_stroke(ctx);
-}
-void test_line_join (VkvgContext ctx){
-    float x = 50, y = 150, dx = 150, dy = 140;
-
-    vkvg_scale(ctx,2,2);
-
-    vkvg_set_line_width(ctx,40);
-    vkvg_set_source_rgba(ctx,0,0,0,1);
-
-
-    vkvg_set_line_join(ctx,VKVG_LINE_JOIN_ROUND);
-    //vkvg_rectangle(ctx,x,y,dx,dy);
-
-    vkvg_move_to(ctx,x,y);
-    vkvg_rel_line_to(ctx,50,-30);
-    vkvg_rel_line_to(ctx,50,0);
-    vkvg_rel_line_to(ctx,50,30);
-    vkvg_rel_line_to(ctx,0,60);
-    vkvg_rel_line_to(ctx,-50,70);
-    vkvg_rel_line_to(ctx,-50,0);
-    vkvg_rel_line_to(ctx,-50,-70);
-    vkvg_close_path(ctx);
-    vkvg_stroke(ctx);
-
-    vkvg_set_source_rgba(ctx,1,0,0,1);
-    vkvg_move_to(ctx,x+200,y);
-    vkvg_rel_line_to(ctx,50,70);
-    vkvg_rel_line_to(ctx,50,0);
-    vkvg_rel_line_to(ctx,50,-70);
-    vkvg_rel_line_to(ctx,0,-60);
-    vkvg_rel_line_to(ctx,-50,-30);
-    vkvg_rel_line_to(ctx,-50,0);
-    vkvg_rel_line_to(ctx,-50,30);
-    vkvg_close_path(ctx);
-    vkvg_stroke(ctx);
-
-    vkvg_move_to(ctx,x,y);
-    vkvg_rel_line_to(ctx,50,-30);
-    vkvg_rel_line_to(ctx,50,0);
-    vkvg_rel_line_to(ctx,50,30);
-    vkvg_rel_line_to(ctx,0,60);
-    vkvg_rel_line_to(ctx,-50,70);
-    vkvg_rel_line_to(ctx,-50,0);
-    vkvg_rel_line_to(ctx,-50,-70);
-    vkvg_close_path(ctx);
-    vkvg_stroke(ctx);
-
-//    vkvg_rel_line_to(ctx,dx,-dy);
-//    vkvg_rel_line_to(ctx,dx,dy);
-//    vkvg_stroke(ctx);
-//    vkvg_set_line_join(ctx,VKVG_LINE_JOIN_BEVEL);
-//    vkvg_rel_move_to(ctx,-dx*2,abs(dy*1.5));
-//    vkvg_rel_line_to(ctx,dx,-dy);
-//    vkvg_rel_line_to(ctx,dx,dy);
-//    vkvg_stroke(ctx);
-//    vkvg_set_line_join(ctx,VKVG_LINE_JOIN_ROUND);
-//    vkvg_rel_move_to(ctx,-dx*2,abs(dy*1.5));
-//    vkvg_rel_line_to(ctx,dx,-dy);
-//    vkvg_rel_line_to(ctx,dx,dy);
-//    vkvg_stroke(ctx);
-//    vkvg_set_line_join(ctx,VKVG_LINE_JOIN_MITER);
-}
-void test_colinear () {
-    VkvgContext ctx = vkvg_create(surf);
-    vkvg_set_source_rgba(ctx,0.7,0.7,0.7,1);
-    vkvg_paint(ctx);
-
-    vkvg_set_source_rgba(ctx,0,0,0,1);
-    vkvg_set_line_width(ctx,10);
-
-    vkvg_move_to(ctx,100,100);
-    vkvg_line_to(ctx,100,200);
-    vkvg_line_to(ctx,100,100);
-    vkvg_stroke(ctx);
-
-    vkvg_destroy(ctx);
-}
-
-void multi_test1 () {
-    VkvgSurface surf2 = vkvg_surface_create (device,800,800);;
-    VkvgContext ctx = vkvg_create (surf2);
-
-    vkvg_set_source_rgba(ctx,0.1,0.1,0.3,1);
-    vkvg_paint(ctx);
-
-    vkvg_test_fill(ctx);
-    vkvg_test_fill2(ctx);
-
-//    vkvg_set_line_join(ctx,VKVG_LINE_JOIN_ROUND);
-
-
-    //test_line_join(ctx);
-//    vkvg_set_source_rgba(ctx,0.0,1.0,0.0,1.0);
-
-//    vkvg_move_to(ctx,50,50);
-//    vkvg_rel_line_to(ctx,400,-10);
-//    vkvg_rel_line_to(ctx,-200,300);
-//    vkvg_close_path(ctx);
-//    vkvg_fill(ctx);
-
-//    vkvg_set_source_rgba(ctx,1.0,1.0,0.0,1.0);
-//    vkvg_move_to(ctx,100,100);
-//    vkvg_line_to(ctx,100,200);
-//    vkvg_stroke(ctx);
-
-    //vkvg_test_clip(ctx);
-
-//    vkvg_set_source_rgba (ctx,0.02,0.8,0.3,1.0);
-//    vkvg_rectangle (ctx,200,200,300,300);
-//    vkvg_fill (ctx);
-
-
-    test_text(ctx);
-
-
-    vkvg_test_stroke(ctx);
-
-    vkvg_translate(ctx, 100,50);
-//    vkvg_rotate(ctx, 0.2);
-    //vkvg_scale(ctx, 2,2);
-
-
-    //vkvg_test_gradient (ctx);
-
-    vkvg_test_curves(ctx);
-    vkvg_test_curves2(ctx);
-
-    /*vkvg_set_operator(ctx, VKVG_OPERATOR_CLEAR);
-    vkvg_rectangle(ctx,100,100,300,300);
-    vkvg_fill(ctx);
-    vkvg_set_operator(ctx, VKVG_OPERATOR_OVER);*/
-
-    //test_img_surface(ctx);
-    //test_line_caps(ctx);
-
-    vkvg_destroy(ctx);
-    ctx = vkvg_create(surf);
-
-    vkvg_set_source_rgba(ctx,1.0,0.0,0.0,1);
-    vkvg_paint(ctx);
-//    vkvg_set_source_rgba(ctx,0.0,0.0,1.0,1);
-//    vkvg_rectangle(ctx,100,100,500,500);
-//    vkvg_fill(ctx);
-
-    VkvgPattern pat = vkvg_pattern_create_for_surface(surf2);
-    vkvg_pattern_set_extend(pat, VKVG_EXTEND_REFLECT);
-    vkvg_pattern_set_filter(pat, VKVG_FILTER_BEST);
-    vkvg_set_source (ctx, pat);
-    //vkvg_rectangle(ctx,100,100,400,400);
-    //vkvg_fill(ctx);
-    vkvg_paint(ctx);
-    vkvg_translate(ctx,200,200);
-    vkvg_paint(ctx);
-    vkvg_rotate(ctx,0.7);
-    vkvg_paint(ctx);
-    vkvg_pattern_destroy (pat);
-    vkvg_destroy(ctx);
-    vkvg_surface_destroy(surf2);
-}
+#include "test.h"
 
 void cairo_test_fill_rule (VkvgContext cr){
     vkvg_set_line_width (cr, 6);
@@ -970,308 +344,9 @@ void cairo_tests () {
     vkvg_destroy(ctx);
 }
 
-void test_grad_transforms () {
-    VkvgContext ctx = vkvg_create(surf);
-
-    vkvg_translate(ctx,-100,-100);
-    vkvg_rotate(ctx,1.5);
-    //vkvg_translate(ctx,100,100);
-
-    //vkvg_scale(ctx,0.2,0.2);
-    VkvgPattern pat = vkvg_pattern_create_linear(0,0,200,0);
-    vkvg_set_line_width(ctx, 20);
-    vkvg_patter_add_color_stop(pat, 0, 1, 0, 0, 1);
-    vkvg_patter_add_color_stop(pat, 0.5, 0, 1, 0, 1);
-    vkvg_patter_add_color_stop(pat, 1, 0, 0, 1, 1);
-    vkvg_set_source (ctx, pat);
-    vkvg_rectangle(ctx,0,0,200,200);
-    vkvg_fill (ctx);
-    //vkvg_stroke (ctx);
-    vkvg_pattern_destroy (pat);
-
-    vkvg_destroy(ctx);
-}
-
-void svg_set_color (VkvgContext ctx, uint32_t c, float alpha) {
-    float a = (c >> 24 & 255) / 255.f;
-    float b = (c >> 16 & 255) / 255.f;
-    float g = (c >> 8 & 255) / 255.f;
-    float r = (c & 255) / 255.f;
-    vkvg_set_source_rgba(ctx,r,g,b,a*alpha);
-}
-
-void test_svg () {
-    rotation+=0.01f;
-
-    vkvg_matrix_t mat;
-    vkvg_matrix_init_translate (&mat, 512,400);
-    vkvg_matrix_rotate(&mat,rotation);
-    vkvg_matrix_translate(&mat,-512,-400);
-
-    VkvgContext ctx = vkvg_create(surf);
-    vkvg_set_source_rgba(ctx,1.0,1.0,1.0,1);
-    vkvg_paint(ctx);
-
-    vkvg_set_matrix(ctx,&mat);
-
-    NSVGimage* svg;
-    NSVGshape* shape;
-    NSVGpath* path;
-    //svg = nsvgParseFromFile("/mnt/data/images/svg/tux.svg", "px", 96);
-    //svg = nsvgParseFromFile("/mnt/data/images/svg/world.svg", "px", 96);
-    svg = nsvgParseFromFile("/mnt/data/images/svg/tiger.svg", "px", 96);
-    //svg = nsvgParseFromFile("/mnt/data/images/svg/koch_curve.svg", "px", 96);
-    //svg = nsvgParseFromFile("/mnt/data/images/svg/diamond1.svg", "px", 96);
-    //svg = nsvgParseFromFile("/mnt/data/images/svg/diamond2.svg", "px", 96);
-    //svg = nsvgParseFromFile("/home/jp/yahweh-protosinaitic.svg", "px", 96);
-    //svg = nsvgParseFromFile("/mnt/data/images/svg/WMD-biological.svg", "px", 96);
-    //svg = nsvgParseFromFile("/mnt/data/images/svg/Skull_and_crossbones.svg", "px", 96);
-    //svg = nsvgParseFromFile("/mnt/data/images/svg/IconAlerte.svg", "px", 96);
-    //svg = nsvgParseFromFile("/mnt/data/images/svg/Svg_example4.svg", "px", 96);
-
-    //vkvg_scale(ctx, 3,3);
-    vkvg_set_source_rgba(ctx,0.0,0.0,0.0,1);
-
-    for (shape = svg->shapes; shape != NULL; shape = shape->next) {
-
-        vkvg_new_path(ctx);
-
-        float o = shape->opacity;
-
-        vkvg_set_line_width(ctx, shape->strokeWidth);
-
-        for (path = shape->paths; path != NULL; path = path->next) {
-            float* p = path->pts;
-            vkvg_move_to(ctx, p[0],p[1]);
-            for (int i = 1; i < path->npts-2; i += 3) {
-                p = &path->pts[i*2];
-                vkvg_curve_to(ctx, p[0],p[1], p[2],p[3], p[4],p[5]);
-            }
-            if (path->closed)
-                vkvg_close_path(ctx);
-        }
-
-        if (shape->fill.type == NSVG_PAINT_COLOR)
-            svg_set_color(ctx, shape->fill.color, o);
-        else if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT){
-            NSVGgradient* g = shape->fill.gradient;
-            svg_set_color(ctx, g->stops[0].color, o);
-        }
-
-        if (shape->fill.type != NSVG_PAINT_NONE){
-            if (shape->stroke.type == NSVG_PAINT_NONE){
-                vkvg_fill(ctx);
-                continue;
-            }
-            vkvg_fill_preserve (ctx);
-        }
-
-        if (shape->stroke.type == NSVG_PAINT_COLOR)
-            svg_set_color(ctx, shape->stroke.color, o);
-        else if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT){
-            NSVGgradient* g = shape->stroke.gradient;
-            svg_set_color(ctx, g->stops[0].color, o);
-        }
-
-        vkvg_stroke(ctx);
-    }
-
-    nsvgDelete(svg);
-
-
-    vkvg_destroy(ctx);
-}
-void test_1 () {
-    const char* text = "This is a petit test {}";
-
-    VkvgContext ctx = vkvg_create (surf);
-
-    vkvg_set_source_rgba(ctx,0,0,0,1.0);
-    vkvg_paint (ctx);
-    vkvg_set_source_rgba(ctx,1,1,1,1.0);
-
-    vkvg_select_font_face(ctx, "mono");
-    vkvg_set_font_size(ctx, 12);
-
-    vkvg_font_extents_t f = {};
-    vkvg_font_extents(ctx, &f);
-
-    vkvg_text_extents_t t = {};
-    vkvg_text_extents(ctx, text, &t);
-
-    vkvg_move_to(ctx,100,100);
-    vkvg_show_text(ctx, text);
-
-    vkvg_move_to(ctx,100,100.5 - f.ascent);
-    vkvg_line_to(ctx,100+t.width,100.5 - f.ascent);
-
-    vkvg_move_to(ctx,100,100.5 - f.descent);
-    vkvg_line_to(ctx,100+t.width,100.5 - f.descent);
-
-    vkvg_stroke(ctx);
-
-    vkvg_flush(ctx);
-    vkvg_destroy (ctx);
-}
-void test_painting () {
-    VkvgSurface surf2 = vkvg_surface_create (device,400,400);;
-    VkvgContext ctx = vkvg_create (surf2);
-
-    vkvg_set_source_rgba(ctx,1.0,0.,0.,1.0);
-    vkvg_paint (ctx);
-
-    vkvg_destroy (ctx);
-    ctx = vkvg_create (surf);
-
-    vkvg_set_source_rgba(ctx,0.1,0.1,0.3,1.0);
-    vkvg_paint (ctx);
-
-    //vkvg_set_source_surface(ctx,surf2,0,0);
-
-    //VkvgPattern pat = vkvg_get_source (ctx);
-    VkvgPattern pat = vkvg_pattern_create_for_surface(surf2);
-    vkvg_pattern_set_extend (pat,VKVG_EXTEND_REFLECT);
-    vkvg_set_source(ctx,pat);
-    //vkvg_paint (ctx);
-    //vkvg_set_source_rgba(ctx,0,1,0,1.0);
-    vkvg_rectangle(ctx,100,100,200,200);
-    vkvg_fill(ctx);
-
-    vkvg_destroy (ctx);
-    vkvg_surface_destroy (surf2);
-    vkvg_pattern_destroy (pat);
-}
-
-void simple_paint () {
-    VkvgContext ctx = vkvg_create(surf);
-    vkvg_set_source_rgba(ctx,1,0,0,1);
-    vkvg_paint(ctx);
-    vkvg_destroy(ctx);
-}
-void random_rectangles () {
-    vkvg_surface_clear(surf);
-    struct timeval currentTime;
-    gettimeofday(&currentTime, NULL);
-
-    srand((unsigned) currentTime.tv_usec);
-    const float w = 1024.f;
-
-    VkvgContext ctx = vkvg_create(surf);
-    vkvg_set_line_width(ctx,1);
-    for (int i=0; i<5000; i++) {
-        randomize_color(ctx);
-        float x = trunc( (0.5*(float)w*rand())/RAND_MAX );
-        float y = trunc( (0.5*(float)w*rand())/RAND_MAX );
-        float z = trunc( (0.5*(float)w*rand())/RAND_MAX ) + 1;
-        float v = trunc( (0.5*(float)w*rand())/RAND_MAX ) + 1;
-
-        vkvg_rectangle(ctx, x+1, y+1, z, v);
-        vkvg_fill_preserve(ctx);
-        randomize_color(ctx);
-        vkvg_stroke(ctx);
-    }
-    vkvg_destroy(ctx);
-}
-
-void simple_rectangle_fill () {
-    vkvg_surface_clear(surf);
-    VkvgContext ctx = vkvg_create(surf);
 
-    vkvg_set_line_width(ctx,10);
-    vkvg_set_source_rgba(ctx,0,0,1,0.5);
-    vkvg_rectangle(ctx,100,100,200,200);
-    vkvg_fill(ctx);
-    vkvg_rectangle(ctx,200,200,200,200);
-    vkvg_set_source_rgba(ctx,1,0,0,0.5);
-    vkvg_fill(ctx);
-    vkvg_destroy(ctx);
-}
-void simple_rectangle_stroke () {
-    VkvgContext ctx = vkvg_create(surf);
-    vkvg_clear(ctx);
-    vkvg_set_line_width(ctx,100.f);
-    vkvg_arc (ctx, 300, 300, 200, 0, M_PI*2.0);
-    vkvg_set_source_rgba(ctx,0,0,1,1);
-    vkvg_fill_preserve(ctx);
-    vkvg_set_source_rgba(ctx,1,0,1,1);
-    vkvg_stroke(ctx);
-    vkvg_destroy(ctx);
-}
-void lines_stroke () {
-    VkvgContext ctx = vkvg_create(surf);
-    vkvg_set_source_rgba(ctx,1,1,1,1);
-    vkvg_paint(ctx);
-    vkvg_set_source_rgba(ctx,0,0,0,0.9f);
-    vkvg_set_line_width(ctx,10.f);
-    vkvg_move_to(ctx,50,50);
-    vkvg_line_to(ctx,300,250);
-    vkvg_stroke(ctx);
-    vkvg_destroy(ctx);
-}
 int main(int argc, char *argv[]) {
-    //dumpLayerExts();
-    uint width=1024, height=768;
-
-    vk_engine_t* e = vkengine_create (VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, VK_PRESENT_MODE_MAILBOX_KHR, width, height);
-    VkhPresenter r = e->renderer;
-    vkengine_set_key_callback (e, key_callback);
-    vkengine_set_mouse_but_callback(e, mouse_button_callback);
-    vkengine_set_cursor_pos_callback(e, mouse_move_callback);
-    vkengine_set_scroll_callback(e, scroll_callback);
-
-    bool deferredResolve = false;
-
-    device  = vkvg_device_create_multisample(vkh_app_get_inst(e->app), r->dev->phy, r->dev->dev, r->qFam, 0, VK_SAMPLE_COUNT_4_BIT, deferredResolve);
-    surf    = vkvg_surface_create(device, width, height);
-
-    //test_grad_transforms();
-    //test_colinear();
-    //simple_rectangle_stroke();
-
-    vkh_presenter_build_blit_cmd (r, vkvg_surface_get_vk_image(surf), width, height);
-
-    struct timeval before , after;
-    double frameTime = 0, frameTimeAccum = 0, frameCount = 0;
-
-    while (!vkengine_should_close (e)) {
-        glfwPollEvents();
-
-        gettimeofday(&before , NULL);
-
-        //test_svg();
-        cairo_tests();
-        //random_rectangles();
-        //simple_rectangle_stroke();
-        //test_1();
-        //vkvg_surface_clear(surf);
-        //simple_paint();
-
-        //simple_rectangle_fill();
-        //test_img_surface();
-        //multi_test1();
-        //test_painting();
-        //vkvg_surface_clear(surf);
-        //lines_stroke();
-        if (deferredResolve)
-            vkvg_multisample_surface_resolve(surf);
-        if (!vkh_presenter_draw (r))
-            vkh_presenter_build_blit_cmd (r, vkvg_surface_get_vk_image(surf), width, height);
-
-        gettimeofday(&after , NULL);
-
-        frameTimeAccum += time_diff(before , after);
-        frameCount++;
-    }
-
-    frameTime = frameTimeAccum / frameCount;
-    printf ("frame (µs): %.0lf\nfps: %lf\n", frameTime, floor(1000000 / frameTime));
-
-    vkDeviceWaitIdle(e->dev->dev);
-
-    vkvg_surface_destroy    (surf);
-    vkvg_device_destroy     (device);
-
-    vkengine_destroy (e);
 
+    perform_test (cairo_tests, 1024, 768);
     return 0;
 }
diff --git a/tests/text.c b/tests/text.c
new file mode 100644 (file)
index 0000000..aad4afa
--- /dev/null
@@ -0,0 +1,96 @@
+#include "test.h"
+
+void test(){
+    VkvgContext ctx = vkvg_create(surf);
+
+    vkvg_set_source_rgba(ctx,0.9,0.9,0.9,1);
+    vkvg_paint(ctx);
+
+    int size = 19;
+    int penY = 50;
+    int penX = 10;
+
+    /*vkvg_rectangle(ctx,30,0,100,400);
+    vkvg_clip(ctx);*/
+
+    //vkvg_select_font_face(ctx, "/usr/local/share/fonts/DroidSansMono.ttf");
+    //vkvg_select_font_face(ctx, "/usr/share/fonts/truetype/unifont/unifont.ttf");
+
+    vkvg_set_font_size(ctx,12);
+    vkvg_select_font_face(ctx, "droid");
+    vkvg_font_extents_t fe;
+    vkvg_font_extents (ctx,&fe);
+    vkvg_move_to(ctx, penX,penY);
+    vkvg_set_source_rgba(ctx,0.1,0.1,0.1,1);
+    vkvg_text_extents_t te;
+    vkvg_text_extents(ctx,"abcdefghijk",&te);
+    vkvg_show_text (ctx,"abcdefghijk");
+    penX+= te.x_advance;
+    vkvg_move_to(ctx, penX,penY);
+    vkvg_show_text (ctx,"*abcdefghijk2");
+    penY+=2*size;
+
+    vkvg_select_font_face(ctx, "times");
+    vkvg_set_source_rgba(ctx,0.1,0.1,0.2,1);
+    vkvg_move_to(ctx, penX,penY);
+    vkvg_show_text (ctx,"abcdefghijklmnopqrstuvwxyz");
+    penY+=size;
+
+    vkvg_select_font_face(ctx, "droid");
+    vkvg_move_to(ctx, penX,penY);
+    vkvg_show_text (ctx,"lmnopqrstuvwxyz123456789");
+    penY+=size;
+
+    vkvg_select_font_face(ctx, "times:bold");
+    vkvg_move_to(ctx, penX,penY);
+    vkvg_show_text (ctx,"abcdefghijklmnopqrstuvwxyz");
+    penY+=size;
+
+    vkvg_select_font_face(ctx, "droid");
+    vkvg_move_to(ctx, penX,penY);
+    vkvg_show_text (ctx,"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+    penY+=size;
+
+    vkvg_select_font_face(ctx, "arial:italic");
+    vkvg_move_to(ctx, penX,penY);
+    vkvg_show_text (ctx,"abcdefghijklmnopqrstuvwxyz");
+    penY+=size;
+
+    vkvg_select_font_face(ctx, "arial");
+    vkvg_move_to(ctx, penX,penY);
+    vkvg_show_text (ctx,"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+    penY+=size;
+    vkvg_move_to(ctx, penX,penY);
+    vkvg_show_text (ctx,"this is a test");
+    penY+=size;
+    vkvg_move_to(ctx, penX,penY);
+    vkvg_show_text (ctx,"this is another test to see if label is working");
+    penY+=size;
+
+    vkvg_select_font_face(ctx, "mono");
+    vkvg_move_to(ctx, penX,penY);
+    vkvg_show_text (ctx,"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+    penY+=size;
+
+    vkvg_move_to(ctx, 80,400);
+    vkvg_show_text (ctx,"Ленивый рыжий кот");
+
+    /*vkvg_move_to(ctx, 150,250);
+    vkvg_show_text (ctx,"test string é€");
+    vkvg_move_to(ctx, 150,300);
+    vkvg_show_text (ctx,"كسول الزنجبيل القط");
+    vkvg_move_to(ctx, 150,350);
+    vkvg_show_text (ctx,"懶惰的姜貓");*/
+
+    //vkvg_show_text (ctx,"ABCDABCD");
+    //vkvg_show_text (ctx,"j");
+
+    vkvg_destroy(ctx);
+}
+
+int main(int argc, char *argv[]) {
+
+    perform_test (test, 1024, 768);
+
+    return 0;
+}
diff --git a/tests/vkengine.c b/tests/vkengine.c
deleted file mode 100644 (file)
index 6d0f0ef..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright (c) 2018 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
- * Software, and to permit persons to whom the Software is furnished to do so, subject
- * to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "vkh.h"
-#include "vkengine.h"
-#include "vkh_app.h"
-#include "vkh_phyinfo.h"
-#include "vkh_presenter.h"
-#include "vkh_image.h"
-#include "vkh_device.h"
-
-bool vkeCheckPhyPropBlitSource (VkEngine e) {
-    VkFormatProperties formatProps;
-    vkGetPhysicalDeviceFormatProperties(e->dev->phy, e->renderer->format, &formatProps);
-
-#if VKVG_TILING_OPTIMAL
-    assert((formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) && "Format cannot be used as transfer source");
-#else
-    assert((formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) && "Format cannot be used as transfer source");
-#endif
-}
-
-VkSampleCountFlagBits getMaxUsableSampleCount(VkSampleCountFlags counts)
-{
-    if (counts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; }
-    if (counts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; }
-    if (counts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; }
-    if (counts & VK_SAMPLE_COUNT_8_BIT) { return VK_SAMPLE_COUNT_8_BIT; }
-    if (counts & VK_SAMPLE_COUNT_4_BIT) { return VK_SAMPLE_COUNT_4_BIT; }
-    if (counts & VK_SAMPLE_COUNT_2_BIT) { return VK_SAMPLE_COUNT_2_BIT; }
-    return VK_SAMPLE_COUNT_1_BIT;
-}
-
-void vkengine_dump_Infos (VkEngine e){
-    printf("max samples = %d\n", getMaxUsableSampleCount(e->gpu_props.limits.framebufferColorSampleCounts));
-    printf("max tex2d size = %d\n", e->gpu_props.limits.maxImageDimension2D);
-    printf("max tex array layers = %d\n", e->gpu_props.limits.maxImageArrayLayers);
-    printf("max mem alloc count = %d\n", e->gpu_props.limits.maxMemoryAllocationCount);
-
-    for (int i = 0; i < e->memory_properties.memoryHeapCount; i++) {
-        printf("Mem Heap %d\n", i);
-        printf("\tflags= %d\n", e->memory_properties.memoryHeaps[i].flags);
-        printf("\tsize = %d Mo\n", e->memory_properties.memoryHeaps[i].size/ (1024*1024));
-    }
-    for (int i = 0; i < e->memory_properties.memoryTypeCount; i++) {
-        printf("Mem type %d\n", i);
-        printf("\theap %d: ", e->memory_properties.memoryTypes[i].heapIndex);
-        if (e->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
-            printf("VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT|");
-        if (e->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
-            printf("VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT|");
-        if (e->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
-            printf("VK_MEMORY_PROPERTY_HOST_COHERENT_BIT|");
-        if (e->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
-            printf("VK_MEMORY_PROPERTY_HOST_CACHED_BIT|");
-        if (e->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT)
-            printf("VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT|");
-        printf("\n");
-    }
-}
-vk_engine_t* vkengine_create (VkPhysicalDeviceType preferedGPU, VkPresentModeKHR presentMode, uint32_t width, uint32_t height) {
-    vk_engine_t* e = (vk_engine_t*)calloc(1,sizeof(vk_engine_t));
-
-    glfwInit();
-    assert (glfwVulkanSupported()==GLFW_TRUE);
-
-    uint32_t enabledExtsCount = 0, phyCount = 0;
-    const char ** enabledExts = glfwGetRequiredInstanceExtensions (&enabledExtsCount);
-
-    e->app = vkh_app_create("vkvgTest", enabledExtsCount, enabledExts);
-
-    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
-    glfwWindowHint(GLFW_RESIZABLE,  GLFW_TRUE);
-    glfwWindowHint(GLFW_FLOATING,   GLFW_FALSE);
-    glfwWindowHint(GLFW_DECORATED,  GLFW_TRUE);
-
-    e->window = glfwCreateWindow ((int)width, (int)height, "Window Title", NULL, NULL);
-
-    VkSurfaceKHR surf;
-    VkResult res = glfwCreateWindowSurface(e->app->inst, e->window, NULL, &surf);
-
-    VkhPhyInfo* phys = vkh_app_get_phyinfos (e->app, &phyCount, surf);
-
-    VkhPhyInfo pi = NULL;
-    for (int i=0; i<phyCount; i++){
-        pi = phys[i];
-        if (pi->properties.deviceType == preferedGPU)
-            break;
-    }
-
-    e->memory_properties = pi->memProps;
-    e->gpu_props = pi->properties;
-
-    uint32_t qCount = 0;
-    VkDeviceQueueCreateInfo pQueueInfos[3];
-    float queue_priorities[] = {0.0};
-
-    VkDeviceQueueCreateInfo qiG = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
-                                   .queueCount = 1,
-                                   .queueFamilyIndex = pi->gQueue,
-                                   .pQueuePriorities = queue_priorities };
-    VkDeviceQueueCreateInfo qiC = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
-                                   .queueCount = 1,
-                                   .queueFamilyIndex = pi->cQueue,
-                                   .pQueuePriorities = queue_priorities };
-    VkDeviceQueueCreateInfo qiT = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
-                                   .queueCount = 1,
-                                   .queueFamilyIndex = pi->tQueue,
-                                   .pQueuePriorities = queue_priorities };
-
-    if (pi->gQueue == pi->cQueue){
-        if(pi->gQueue == pi->tQueue){
-            qCount=1;
-            pQueueInfos[0] = qiG;
-        }else{
-            qCount=2;
-            pQueueInfos[0] = qiG;
-            pQueueInfos[1] = qiT;
-        }
-    }else{
-        if((pi->gQueue == pi->tQueue) || (pi->cQueue==pi->tQueue)){
-            qCount=2;
-            pQueueInfos[0] = qiG;
-            pQueueInfos[1] = qiC;
-        }else{
-            qCount=3;
-            pQueueInfos[0] = qiG;
-            pQueueInfos[1] = qiC;
-            pQueueInfos[2] = qiT;
-        }
-    }
-
-    char const * dex [] = {"VK_KHR_swapchain"};
-
-    VkPhysicalDeviceFeatures enabledFeatures = {
-        .fillModeNonSolid = true,
-        //.sampleRateShading = true
-    };
-
-    VkDeviceCreateInfo device_info = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
-                                       .queueCreateInfoCount = qCount,
-                                       .pQueueCreateInfos = (VkDeviceQueueCreateInfo*)&pQueueInfos,
-                                       .enabledExtensionCount = 1,
-                                       .ppEnabledExtensionNames = dex,
-                                       .pEnabledFeatures = &enabledFeatures
-                                     };
-
-    VkDevice dev;
-    VK_CHECK_RESULT(vkCreateDevice (pi->phy, &device_info, NULL, &dev));
-    e->dev = vkh_device_create(pi->phy, dev);
-    e->dev->instance = vkh_app_get_inst (e->app);
-
-    e->renderer = vkh_presenter_create
-            (e->dev, (uint32_t) pi->pQueue, surf, width, height, VK_FORMAT_B8G8R8A8_UNORM, presentMode);
-
-    vkh_app_free_phyinfos (phyCount, phys);
-
-    vkeCheckPhyPropBlitSource (e);
-
-    return e;
-}
-
-void vkengine_destroy (VkEngine e) {
-    vkDeviceWaitIdle(e->dev->dev);
-
-    VkSurfaceKHR surf = e->renderer->surface;
-
-    vkh_presenter_destroy (e->renderer);
-    vkDestroySurfaceKHR (e->app->inst, surf, NULL);
-
-    vkh_device_destroy (e->dev);
-
-    glfwDestroyWindow (e->window);
-    glfwTerminate ();
-
-    vkh_app_destroy (e->app);
-
-    free(e);
-}
-void vkengine_close (VkEngine e) {
-    glfwSetWindowShouldClose(e->window, GLFW_TRUE);
-}
-void vkengine_blitter_run (VkEngine e, VkImage img, uint32_t width, uint32_t height) {
-    VkhPresenter p = e->renderer;
-    vkh_presenter_build_blit_cmd (p, img, width, height);
-
-    while (!vkengine_should_close (e)) {
-        glfwPollEvents();
-        if (!vkh_presenter_draw (p))
-            vkh_presenter_build_blit_cmd (p, img, width, height);
-    }
-}
-inline bool vkengine_should_close (VkEngine e) {
-    return glfwWindowShouldClose (e->window);
-}
-
-VkDevice vkengine_get_device (VkEngine e){
-    return e->dev->dev;
-}
-VkPhysicalDevice vkengine_get_physical_device (VkEngine e){
-    return e->dev->phy;
-}
-VkQueue vkengine_get_queue (VkEngine e){
-    return e->renderer->queue;
-}
-uint32_t vkengine_get_queue_fam_idx (VkEngine e){
-    return e->renderer->qFam;
-}
-
-void vkengine_set_key_callback (VkEngine e, GLFWkeyfun key_callback){
-    glfwSetKeyCallback (e->window, key_callback);
-}
-void vkengine_set_mouse_but_callback (VkEngine e, GLFWmousebuttonfun onMouseBut){
-    glfwSetMouseButtonCallback(e->window, onMouseBut);
-}
-void vkengine_set_cursor_pos_callback (VkEngine e, GLFWcursorposfun onMouseMove){
-    glfwSetCursorPosCallback(e->window, onMouseMove);
-}
-void vkengine_set_scroll_callback (VkEngine e, GLFWscrollfun onScroll){
-    glfwSetScrollCallback(e->window, onScroll);
-}
-void vkengine_set_char_callback (VkEngine e, GLFWcharfun onChar){
-    glfwSetCharCallback(e->window, onChar);
-}
-
diff --git a/tests/vkengine.h b/tests/vkengine.h
deleted file mode 100644 (file)
index b4505f8..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2018 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
- * Software, and to permit persons to whom the Software is furnished to do so, subject
- * to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#ifndef VKENGINE_H
-#define VKENGINE_H
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-#include <stdbool.h>
-
-#include <GLFW/glfw3.h>
-#include <vulkan/vulkan.h>
-
-#include "vkh.h"
-
-/* Number of samples needs to be the same at image creation,      */
-/* renderpass creation and pipeline creation.                     */
-#define FENCE_TIMEOUT 100000000
-
-typedef struct _vk_engine_t* VkEngine;
-
-typedef struct _vk_engine_t {
-    VkhApp              app;
-    VkPhysicalDeviceMemoryProperties    memory_properties;
-    VkPhysicalDeviceProperties          gpu_props;
-    VkhDevice           dev;
-    GLFWwindow*         window;
-    VkhPresenter        renderer;
-}vk_engine_t;
-
-vk_engine_t*   vkengine_create  (VkPhysicalDeviceType preferedGPU, VkPresentModeKHR presentMode, uint32_t width, uint32_t height);
-
-void vkengine_destroy       (VkEngine e);
-bool vkengine_should_close  (VkEngine e);
-void vkengine_close         (VkEngine e);
-void vkengine_dump_Infos    (VkEngine e);
-VkDevice            vkengine_get_device         (VkEngine e);
-VkPhysicalDevice    vkengine_get_physical_device(VkEngine e);
-VkQueue             vkengine_get_queue          (VkEngine e);
-uint32_t            vkengine_get_queue_fam_idx  (VkEngine e);
-
-void vkengine_get_queues_properties (vk_engine_t* e, VkQueueFamilyProperties** qFamProps, uint32_t* count);
-
-void vkengine_set_key_callback          (VkEngine e, GLFWkeyfun key_callback);
-void vkengine_set_mouse_but_callback    (VkEngine e, GLFWmousebuttonfun onMouseBut);
-void vkengine_set_cursor_pos_callback   (VkEngine e, GLFWcursorposfun onMouseMove);
-void vkengine_set_scroll_callback       (VkEngine e, GLFWscrollfun onScroll);
-void vkengine_set_char_callback         (VkEngine e, GLFWcharfun onChar);
-#endif