You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

221 lines
7.1KB

  1. #include "widgets.hpp"
  2. // #define DEBUG_ONLY(x) x
  3. #define DEBUG_ONLY(x)
  4. namespace rack {
  5. static NVGcolor getNVGColor(uint32_t color) {
  6. return nvgRGBA(
  7. (color >> 0) & 0xff,
  8. (color >> 8) & 0xff,
  9. (color >> 16) & 0xff,
  10. (color >> 24) & 0xff);
  11. }
  12. static NVGpaint getPaint(NVGcontext *vg, NSVGpaint *p) {
  13. assert(p->type == NSVG_PAINT_LINEAR_GRADIENT || p->type == NSVG_PAINT_RADIAL_GRADIENT);
  14. NSVGgradient *g = p->gradient;
  15. assert(g->nstops >= 1);
  16. NVGcolor icol = getNVGColor(g->stops[0].color);
  17. NVGcolor ocol = getNVGColor(g->stops[g->nstops - 1].color);
  18. float inverse[6];
  19. nvgTransformInverse(inverse, g->xform);
  20. DEBUG_ONLY(printf(" inverse: %f %f %f %f %f %f\n", inverse[0], inverse[1], inverse[2], inverse[3], inverse[4], inverse[5]);)
  21. Vec s, e;
  22. DEBUG_ONLY(printf(" sx: %f sy: %f ex: %f ey: %f\n", s.x, s.y, e.x, e.y);)
  23. // Is it always the case that the gradient should be transformed from (0, 0) to (0, 1)?
  24. nvgTransformPoint(&s.x, &s.y, inverse, 0, 0);
  25. nvgTransformPoint(&e.x, &e.y, inverse, 0, 1);
  26. DEBUG_ONLY(printf(" sx: %f sy: %f ex: %f ey: %f\n", s.x, s.y, e.x, e.y);)
  27. NVGpaint paint;
  28. if (p->type == NSVG_PAINT_LINEAR_GRADIENT)
  29. paint = nvgLinearGradient(vg, s.x, s.y, e.x, e.y, icol, ocol);
  30. else
  31. paint = nvgRadialGradient(vg, s.x, s.y, 0.0, 160, icol, ocol);
  32. return paint;
  33. }
  34. /** Returns the parameterized value of the line p2--p3 where it intersects with p0--p1 */
  35. static float getLineCrossing(Vec p0, Vec p1, Vec p2, Vec p3) {
  36. Vec b = p2.minus(p0);
  37. Vec d = p1.minus(p0);
  38. Vec e = p3.minus(p2);
  39. float m = d.x * e.y - d.y * e.x;
  40. // Check if lines are parallel, or if either pair of points are equal
  41. if (fabsf(m) < 1e-6)
  42. return NAN;
  43. return -(d.x * b.y - d.y * b.x) / m;
  44. }
  45. static void drawSVG(NVGcontext *vg, NSVGimage *svg) {
  46. DEBUG_ONLY(printf("new image: %g x %g px\n", svg->width, svg->height);)
  47. int shapeIndex = 0;
  48. // Iterate shape linked list
  49. for (NSVGshape *shape = svg->shapes; shape; shape = shape->next, shapeIndex++) {
  50. 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]);)
  51. // Visibility
  52. if (!(shape->flags & NSVG_FLAGS_VISIBLE))
  53. continue;
  54. nvgSave(vg);
  55. // Opacity
  56. if (shape->opacity < 1.0)
  57. nvgGlobalAlpha(vg, shape->opacity);
  58. // Build path
  59. nvgBeginPath(vg);
  60. // Iterate path linked list
  61. for (NSVGpath *path = shape->paths; path; path = path->next) {
  62. 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]);)
  63. nvgMoveTo(vg, path->pts[0], path->pts[1]);
  64. for (int i = 1; i < path->npts; i += 3) {
  65. float *p = &path->pts[2*i];
  66. nvgBezierTo(vg, p[0], p[1], p[2], p[3], p[4], p[5]);
  67. // nvgLineTo(vg, p[4], p[5]);
  68. DEBUG_ONLY(printf(" bezier (%f, %f) to (%f, %f)\n", p[-2], p[-1], p[4], p[5]);)
  69. }
  70. // Close path
  71. if (path->closed)
  72. nvgClosePath(vg);
  73. // Compute whether this is a hole or a solid.
  74. // Assume that no paths are crossing (usually true for normal SVG graphics).
  75. // Also assume that the topology is the same if we use straight lines rather than Beziers (not always the case but usually true).
  76. // 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).
  77. int crossings = 0;
  78. Vec p0 = Vec(path->pts[0], path->pts[1]);
  79. Vec p1 = Vec(path->bounds[0] - 1.0, path->bounds[1] - 1.0);
  80. // Iterate all other paths
  81. for (NSVGpath *path2 = shape->paths; path2; path2 = path2->next) {
  82. if (path2 == path)
  83. continue;
  84. // Iterate all lines on the path
  85. if (path2->npts < 4)
  86. continue;
  87. for (int i = 1; i < path2->npts + 3; i += 3) {
  88. float *p = &path2->pts[2*i];
  89. // The previous point
  90. Vec p2 = Vec(p[-2], p[-1]);
  91. // The current point
  92. Vec p3 = (i < path2->npts) ? Vec(p[4], p[5]) : Vec(path2->pts[0], path2->pts[1]);
  93. float crossing = getLineCrossing(p0, p1, p2, p3);
  94. float crossing2 = getLineCrossing(p2, p3, p0, p1);
  95. if (0.0 <= crossing && crossing < 1.0 && 0.0 <= crossing2) {
  96. crossings++;
  97. }
  98. }
  99. }
  100. if (crossings % 2 == 0)
  101. nvgPathWinding(vg, NVG_SOLID);
  102. else
  103. nvgPathWinding(vg, NVG_HOLE);
  104. /*
  105. // Shoelace algorithm for computing the area, and thus the winding direction
  106. float area = 0.0;
  107. Vec p0 = Vec(path->pts[0], path->pts[1]);
  108. for (int i = 1; i < path->npts; i += 3) {
  109. float *p = &path->pts[2*i];
  110. Vec p1 = (i < path->npts) ? Vec(p[4], p[5]) : Vec(path->pts[0], path->pts[1]);
  111. area += 0.5 * (p1.x - p0.x) * (p1.y + p0.y);
  112. printf("%f %f, %f %f\n", p0.x, p0.y, p1.x, p1.y);
  113. p0 = p1;
  114. }
  115. printf("%f\n", area);
  116. if (area < 0.0)
  117. nvgPathWinding(vg, NVG_CCW);
  118. else
  119. nvgPathWinding(vg, NVG_CW);
  120. */
  121. }
  122. // Fill shape
  123. if (shape->fill.type) {
  124. switch (shape->fill.type) {
  125. case NSVG_PAINT_COLOR: {
  126. NVGcolor color = getNVGColor(shape->fill.color);
  127. nvgFillColor(vg, color);
  128. DEBUG_ONLY(printf(" fill color (%g, %g, %g, %g)\n", color.r, color.g, color.b, color.a);)
  129. } break;
  130. case NSVG_PAINT_LINEAR_GRADIENT:
  131. case NSVG_PAINT_RADIAL_GRADIENT: {
  132. NSVGgradient *g = shape->fill.gradient;
  133. (void)g;
  134. DEBUG_ONLY(printf(" gradient: type: %s xform: %f %f %f %f %f %f spread: %d fx: %f fy: %f nstops: %d\n", (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT ? "linear" : "radial"), g->xform[0], g->xform[1], g->xform[2], g->xform[3], g->xform[4], g->xform[5], g->spread, g->fx, g->fy, g->nstops);)
  135. for (int i = 0; i < g->nstops; i++) {
  136. DEBUG_ONLY(printf(" stop: #%08x\t%f\n", g->stops[i].color, g->stops[i].offset);)
  137. }
  138. nvgFillPaint(vg, getPaint(vg, &shape->fill));
  139. } break;
  140. }
  141. nvgFill(vg);
  142. }
  143. // Stroke shape
  144. if (shape->stroke.type) {
  145. nvgStrokeWidth(vg, shape->strokeWidth);
  146. // strokeDashOffset, strokeDashArray, strokeDashCount not yet supported
  147. nvgLineCap(vg, (NVGlineCap) shape->strokeLineCap);
  148. nvgLineJoin(vg, (int) shape->strokeLineJoin);
  149. switch (shape->stroke.type) {
  150. case NSVG_PAINT_COLOR: {
  151. NVGcolor color = getNVGColor(shape->stroke.color);
  152. nvgStrokeColor(vg, color);
  153. DEBUG_ONLY(printf(" stroke color (%g, %g, %g, %g)\n", color.r, color.g, color.b, color.a);)
  154. } break;
  155. case NSVG_PAINT_LINEAR_GRADIENT: {
  156. // NSVGgradient *g = shape->stroke.gradient;
  157. // printf(" lin grad: %f\t%f\n", g->fx, g->fy);
  158. } break;
  159. }
  160. nvgStroke(vg);
  161. }
  162. nvgRestore(vg);
  163. // if (std::string(shape->id) == "rect19347")
  164. // exit(0);
  165. }
  166. DEBUG_ONLY(printf("\n");)
  167. }
  168. void SVGWidget::wrap() {
  169. if (svg && svg->handle) {
  170. box.size = Vec(svg->handle->width, svg->handle->height);
  171. }
  172. else {
  173. box.size = Vec();
  174. }
  175. }
  176. void SVGWidget::setSVG(std::shared_ptr<SVG> svg) {
  177. this->svg = svg;
  178. wrap();
  179. }
  180. void SVGWidget::draw(NVGcontext *vg) {
  181. if (svg && svg->handle) {
  182. // printf("drawing svg %f %f\n", box.size.x, box.size.y);
  183. drawSVG(vg, svg->handle);
  184. }
  185. }
  186. } // namespace rack