2
0

resolve-shape-geometry.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. #include "resolve-shape-geometry.h"
  2. #ifdef MSDFGEN_USE_SKIA
  3. #include <skia/core/SkPath.h>
  4. #include <skia/pathops/SkPathOps.h>
  5. #include "../core/arithmetics.hpp"
  6. #include "../core/Vector2.hpp"
  7. #include "../core/edge-segments.h"
  8. #include "../core/Contour.h"
  9. namespace msdfgen {
  10. SkPoint pointToSkiaPoint(Point2 p) {
  11. return SkPoint::Make((SkScalar) p.x, (SkScalar) p.y);
  12. }
  13. Point2 pointFromSkiaPoint(const SkPoint p) {
  14. return Point2((double) p.x(), (double) p.y());
  15. }
  16. void shapeToSkiaPath(SkPath &skPath, const Shape &shape) {
  17. for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
  18. if (!contour->edges.empty()) {
  19. const EdgeSegment *edge = contour->edges.back();
  20. skPath.moveTo(pointToSkiaPoint(*edge->controlPoints()));
  21. for (std::vector<EdgeHolder>::const_iterator nextEdge = contour->edges.begin(); nextEdge != contour->edges.end(); edge = *nextEdge++) {
  22. const Point2 *p = edge->controlPoints();
  23. switch (edge->type()) {
  24. case (int) LinearSegment::EDGE_TYPE:
  25. skPath.lineTo(pointToSkiaPoint(p[1]));
  26. break;
  27. case (int) QuadraticSegment::EDGE_TYPE:
  28. skPath.quadTo(pointToSkiaPoint(p[1]), pointToSkiaPoint(p[2]));
  29. break;
  30. case (int) CubicSegment::EDGE_TYPE:
  31. skPath.cubicTo(pointToSkiaPoint(p[1]), pointToSkiaPoint(p[2]), pointToSkiaPoint(p[3]));
  32. break;
  33. }
  34. }
  35. }
  36. }
  37. }
  38. void shapeFromSkiaPath(Shape &shape, const SkPath &skPath) {
  39. shape.contours.clear();
  40. Contour *contour = &shape.addContour();
  41. SkPath::Iter pathIterator(skPath, true);
  42. SkPoint edgePoints[4];
  43. for (SkPath::Verb op; (op = pathIterator.next(edgePoints)) != SkPath::kDone_Verb;) {
  44. switch (op) {
  45. case SkPath::kMove_Verb:
  46. if (!contour->edges.empty())
  47. contour = &shape.addContour();
  48. break;
  49. case SkPath::kLine_Verb:
  50. contour->addEdge(EdgeHolder(pointFromSkiaPoint(edgePoints[0]), pointFromSkiaPoint(edgePoints[1])));
  51. break;
  52. case SkPath::kQuad_Verb:
  53. contour->addEdge(EdgeHolder(pointFromSkiaPoint(edgePoints[0]), pointFromSkiaPoint(edgePoints[1]), pointFromSkiaPoint(edgePoints[2])));
  54. break;
  55. case SkPath::kCubic_Verb:
  56. contour->addEdge(EdgeHolder(pointFromSkiaPoint(edgePoints[0]), pointFromSkiaPoint(edgePoints[1]), pointFromSkiaPoint(edgePoints[2]), pointFromSkiaPoint(edgePoints[3])));
  57. break;
  58. case SkPath::kConic_Verb:
  59. {
  60. SkPoint quadPoints[5];
  61. SkPath::ConvertConicToQuads(edgePoints[0], edgePoints[1], edgePoints[2], pathIterator.conicWeight(), quadPoints, 1);
  62. contour->addEdge(EdgeHolder(pointFromSkiaPoint(quadPoints[0]), pointFromSkiaPoint(quadPoints[1]), pointFromSkiaPoint(quadPoints[2])));
  63. contour->addEdge(EdgeHolder(pointFromSkiaPoint(quadPoints[2]), pointFromSkiaPoint(quadPoints[3]), pointFromSkiaPoint(quadPoints[4])));
  64. }
  65. break;
  66. case SkPath::kClose_Verb:
  67. case SkPath::kDone_Verb:
  68. break;
  69. }
  70. }
  71. if (contour->edges.empty())
  72. shape.contours.pop_back();
  73. }
  74. static void pruneCrossedQuadrilaterals(Shape &shape) {
  75. int n = 0;
  76. for (int i = 0; i < (int) shape.contours.size(); ++i) {
  77. Contour &contour = shape.contours[i];
  78. if (
  79. contour.edges.size() == 4 &&
  80. contour.edges[0]->type() == (int) LinearSegment::EDGE_TYPE &&
  81. contour.edges[1]->type() == (int) LinearSegment::EDGE_TYPE &&
  82. contour.edges[2]->type() == (int) LinearSegment::EDGE_TYPE &&
  83. contour.edges[3]->type() == (int) LinearSegment::EDGE_TYPE && (
  84. sign(crossProduct(contour.edges[0]->direction(1), contour.edges[1]->direction(0)))+
  85. sign(crossProduct(contour.edges[1]->direction(1), contour.edges[2]->direction(0)))+
  86. sign(crossProduct(contour.edges[2]->direction(1), contour.edges[3]->direction(0)))+
  87. sign(crossProduct(contour.edges[3]->direction(1), contour.edges[0]->direction(0)))
  88. ) == 0
  89. ) {
  90. contour.edges.clear();
  91. } else {
  92. if (i != n) {
  93. #ifdef MSDFGEN_USE_CPP11
  94. shape.contours[n] = (Contour &&) contour;
  95. #else
  96. shape.contours[n] = contour;
  97. #endif
  98. }
  99. ++n;
  100. }
  101. }
  102. shape.contours.resize(n);
  103. }
  104. bool resolveShapeGeometry(Shape &shape) {
  105. SkPath skPath;
  106. shape.normalize();
  107. shapeToSkiaPath(skPath, shape);
  108. if (!Simplify(skPath, &skPath))
  109. return false;
  110. // Skia's AsWinding doesn't seem to work for unknown reasons
  111. shapeFromSkiaPath(shape, skPath);
  112. // In some rare cases, Skia produces tiny residual crossed quadrilateral contours, which are not valid geometry, so they must be removed.
  113. pruneCrossedQuadrilaterals(shape);
  114. shape.orientContours();
  115. return true;
  116. }
  117. }
  118. #endif