Ver código fonte

Skia bug workaround, savePng crash fix

Chlumsky 1 ano atrás
pai
commit
5dc5f6260b
2 arquivos alterados com 42 adições e 5 exclusões
  1. 40 4
      ext/resolve-shape-geometry.cpp
  2. 2 1
      ext/save-png.cpp

+ 40 - 4
ext/resolve-shape-geometry.cpp

@@ -5,6 +5,7 @@
 
 #include <skia/core/SkPath.h>
 #include <skia/pathops/SkPathOps.h>
+#include "../core/arithmetics.hpp"
 #include "../core/Vector2.hpp"
 #include "../core/edge-segments.h"
 #include "../core/Contour.h"
@@ -22,10 +23,11 @@ Point2 pointFromSkiaPoint(const SkPoint p) {
 void shapeToSkiaPath(SkPath &skPath, const Shape &shape) {
     for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
         if (!contour->edges.empty()) {
-            skPath.moveTo(pointToSkiaPoint(contour->edges.front()->point(0)));
-            for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
-                const Point2 *p = (*edge)->controlPoints();
-                switch ((*edge)->type()) {
+            const EdgeSegment *edge = contour->edges.back();
+            skPath.moveTo(pointToSkiaPoint(*edge->controlPoints()));
+            for (std::vector<EdgeHolder>::const_iterator nextEdge = contour->edges.begin(); nextEdge != contour->edges.end(); edge = *nextEdge++) {
+                const Point2 *p = edge->controlPoints();
+                switch (edge->type()) {
                     case (int) LinearSegment::EDGE_TYPE:
                         skPath.lineTo(pointToSkiaPoint(p[1]));
                         break;
@@ -78,13 +80,47 @@ void shapeFromSkiaPath(Shape &shape, const SkPath &skPath) {
         shape.contours.pop_back();
 }
 
+static void pruneCrossedQuadrilaterals(Shape &shape) {
+    int n = 0;
+    for (int i = 0; i < (int) shape.contours.size(); ++i) {
+        Contour &contour = shape.contours[i];
+        if (
+            contour.edges.size() == 4 &&
+            contour.edges[0]->type() == (int) LinearSegment::EDGE_TYPE &&
+            contour.edges[1]->type() == (int) LinearSegment::EDGE_TYPE &&
+            contour.edges[2]->type() == (int) LinearSegment::EDGE_TYPE &&
+            contour.edges[3]->type() == (int) LinearSegment::EDGE_TYPE && (
+                sign(crossProduct(contour.edges[0]->direction(1), contour.edges[1]->direction(0)))+
+                sign(crossProduct(contour.edges[1]->direction(1), contour.edges[2]->direction(0)))+
+                sign(crossProduct(contour.edges[2]->direction(1), contour.edges[3]->direction(0)))+
+                sign(crossProduct(contour.edges[3]->direction(1), contour.edges[0]->direction(0)))
+            ) == 0
+        ) {
+            contour.edges.clear();
+        } else {
+            if (i != n) {
+                #ifdef MSDFGEN_USE_CPP11
+                    shape.contours[n] = (Contour &&) contour;
+                #else
+                    shape.contours[n] = contour;
+                #endif
+            }
+            ++n;
+        }
+    }
+    shape.contours.resize(n);
+}
+
 bool resolveShapeGeometry(Shape &shape) {
     SkPath skPath;
+    shape.normalize();
     shapeToSkiaPath(skPath, shape);
     if (!Simplify(skPath, &skPath))
         return false;
     // Skia's AsWinding doesn't seem to work for unknown reasons
     shapeFromSkiaPath(shape, skPath);
+    // In some rare cases, Skia produces tiny residual crossed quadrilateral contours, which are not valid geometry, so they must be removed.
+    pruneCrossedQuadrilaterals(shape);
     shape.orientContours();
     return true;
 }

+ 2 - 1
ext/save-png.cpp

@@ -22,7 +22,8 @@ public:
     inline PngGuard(png_structp png, png_infop info) : png(png), info(info), file(NULL) { }
     inline ~PngGuard() {
         png_destroy_write_struct(&png, &info);
-        fclose(file);
+        if (file)
+            fclose(file);
     }
     inline void setFile(FILE *file) {
         this->file = file;