|  |  | @@ -1,8 +1,8 @@ | 
		
	
		
			
			|  |  |  | #include "widgets.hpp" | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // #define DEBUG_ONLY(x) x | 
		
	
		
			
			|  |  |  | #define DEBUG_ONLY(x) | 
		
	
		
			
			|  |  |  | #define DEBUG_ONLY(x) x | 
		
	
		
			
			|  |  |  | // #define DEBUG_ONLY(x) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | namespace rack { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | @@ -15,9 +15,22 @@ static NVGcolor getNVGColor(uint32_t color) { | 
		
	
		
			
			|  |  |  | (color >> 24) & 0xff); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /** Returns the parameterized value of the line p2--p3 where it intersects with p0--p1 */ | 
		
	
		
			
			|  |  |  | static float getLineCrossing(Vec p0, Vec p1, Vec p2, Vec p3) { | 
		
	
		
			
			|  |  |  | Vec b = p2.minus(p0); | 
		
	
		
			
			|  |  |  | Vec d = p1.minus(p0); | 
		
	
		
			
			|  |  |  | Vec e = p3.minus(p2); | 
		
	
		
			
			|  |  |  | float m = d.x * e.y - d.y * e.x; | 
		
	
		
			
			|  |  |  | // Check if lines are parallel, or if either pair of points are equal | 
		
	
		
			
			|  |  |  | if (fabsf(m) < 1e-6) | 
		
	
		
			
			|  |  |  | return NAN; | 
		
	
		
			
			|  |  |  | return -(d.x * b.y - d.y * b.x) / m; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | 
		
	
		
			
			|  |  |  | DEBUG_ONLY(printf("new image: %g x %g px\n", svg->width, svg->height);) | 
		
	
		
			
			|  |  |  | int shapeIndex = 0; | 
		
	
		
			
			|  |  |  | // Iterate shape linked list | 
		
	
		
			
			|  |  |  | for (NSVGshape *shape = svg->shapes; shape; shape = shape->next, shapeIndex++) { | 
		
	
		
			
			|  |  |  | DEBUG_ONLY(printf("	new shape: %d id \"%s\", fillrule %d, from (%f, %f) to (%f, %f)\n", shapeIndex, shape->id, shape->fillRule, shape->bounds[0], shape->bounds[1], shape->bounds[2], shape->bounds[3]);) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | @@ -26,11 +39,15 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | nvgSave(vg); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Only evenodd fillrule is supported | 
		
	
		
			
			|  |  |  | if (shape->fillRule == NSVG_FILLRULE_EVENODD) {} | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if (shape->opacity < 1.0) | 
		
	
		
			
			|  |  |  | nvgGlobalAlpha(vg, shape->opacity); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Build path | 
		
	
		
			
			|  |  |  | nvgBeginPath(vg); | 
		
	
		
			
			|  |  |  | // Iterate path linked list | 
		
	
		
			
			|  |  |  | for (NSVGpath *path = shape->paths; path; path = path->next) { | 
		
	
		
			
			|  |  |  | DEBUG_ONLY(printf("		new path: %d points, %s, from (%f, %f) to (%f, %f)\n", path->npts, path->closed ? "closed" : "open", path->bounds[0], path->bounds[1], path->bounds[2], path->bounds[3]);) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | @@ -39,14 +56,42 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | 
		
	
		
			
			|  |  |  | float *p = &path->pts[2*i]; | 
		
	
		
			
			|  |  |  | nvgBezierTo(vg, p[0], p[1], p[2], p[3], p[4], p[5]); | 
		
	
		
			
			|  |  |  | // nvgLineTo(vg, p[4], p[5]); | 
		
	
		
			
			|  |  |  | DEBUG_ONLY(printf("			bezier (%f, %f) to (%f, %f)\n", p[-2], p[-1], p[4], p[5]);) | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if (path->closed) | 
		
	
		
			
			|  |  |  | nvgClosePath(vg); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // TODO | 
		
	
		
			
			|  |  |  | // This is totally wrong but works by coincidence | 
		
	
		
			
			|  |  |  | if (!path->next) | 
		
	
		
			
			|  |  |  | // Compute whether this is a hole or a solid. | 
		
	
		
			
			|  |  |  | // Assume that no paths are crossing (usually true for normal SVG graphics). | 
		
	
		
			
			|  |  |  | // Also assume that the topology is the same if we use straight lines rather than Beziers (not always the case but usually true). | 
		
	
		
			
			|  |  |  | // Using the even-odd fill rule, if we draw a line from a point on the path to a point outside the boundary (e.g. top left) and count the number of times it crosses another path, the parity of this count determines whether the path is a hole (odd) or solid (even). | 
		
	
		
			
			|  |  |  | int crossings = 0; | 
		
	
		
			
			|  |  |  | Vec p0 = Vec(path->pts[0], path->pts[1]); | 
		
	
		
			
			|  |  |  | Vec p1 = Vec(path->bounds[0] - 1.0, path->bounds[1] - 1.0); | 
		
	
		
			
			|  |  |  | // Iterate all other paths | 
		
	
		
			
			|  |  |  | for (NSVGpath *path2 = shape->paths; path2; path2 = path2->next) { | 
		
	
		
			
			|  |  |  | if (path2 == path) | 
		
	
		
			
			|  |  |  | continue; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Iterate all lines on the path | 
		
	
		
			
			|  |  |  | if (path2->npts < 4) | 
		
	
		
			
			|  |  |  | continue; | 
		
	
		
			
			|  |  |  | for (int i = 1; i < path2->npts + 3; i += 3) { | 
		
	
		
			
			|  |  |  | float *p = &path2->pts[2*i]; | 
		
	
		
			
			|  |  |  | // The previous point | 
		
	
		
			
			|  |  |  | Vec p2 = Vec(p[-2], p[-1]); | 
		
	
		
			
			|  |  |  | // The current point | 
		
	
		
			
			|  |  |  | Vec p3 = (i < path2->npts) ? Vec(p[4], p[5]) : Vec(path2->pts[0], path2->pts[1]); | 
		
	
		
			
			|  |  |  | float crossing = getLineCrossing(p0, p1, p2, p3); | 
		
	
		
			
			|  |  |  | float crossing2 = getLineCrossing(p2, p3, p0, p1); | 
		
	
		
			
			|  |  |  | if (0.0 <= crossing && crossing < 1.0 && 0.0 <= crossing2) { | 
		
	
		
			
			|  |  |  | crossings++; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if (crossings % 2 == 0) | 
		
	
		
			
			|  |  |  | nvgPathWinding(vg, NVG_SOLID); | 
		
	
		
			
			|  |  |  | else | 
		
	
		
			
			|  |  |  | nvgPathWinding(vg, NVG_HOLE); | 
		
	
	
		
			
				|  |  | @@ -62,6 +107,7 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | 
		
	
		
			
			|  |  |  | } break; | 
		
	
		
			
			|  |  |  | case NSVG_PAINT_LINEAR_GRADIENT: { | 
		
	
		
			
			|  |  |  | NSVGgradient *g = shape->fill.gradient; | 
		
	
		
			
			|  |  |  | (void)g; | 
		
	
		
			
			|  |  |  | DEBUG_ONLY(printf("		linear gradient: %f\t%f\n", g->fx, g->fy);) | 
		
	
		
			
			|  |  |  | } break; | 
		
	
		
			
			|  |  |  | case NSVG_PAINT_RADIAL_GRADIENT: { | 
		
	
	
		
			
				|  |  | @@ -75,7 +121,7 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | 
		
	
		
			
			|  |  |  | NVGcolor color1 = getNVGColor(g->stops[g->nstops - 1].color); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | float inverse[6]; | 
		
	
		
			
			|  |  |  | Rect shapeBox = Rect::fromMinMax(Vec(shape->bounds[0], shape->bounds[1]), Vec(shape->bounds[2], shape->bounds[3])); | 
		
	
		
			
			|  |  |  | // Rect shapeBox = Rect::fromMinMax(Vec(shape->bounds[0], shape->bounds[1]), Vec(shape->bounds[2], shape->bounds[3])); | 
		
	
		
			
			|  |  |  | nvgTransformInverse(inverse, g->xform); | 
		
	
		
			
			|  |  |  | Vec c; | 
		
	
		
			
			|  |  |  | nvgTransformPoint(&c.x, &c.y, inverse, 5, 5); | 
		
	
	
		
			
				|  |  | 
 |