diff --git a/gm/collapsepaths.cpp b/gm/collapsepaths.cpp new file mode 100644 index 0000000000..22ddf9bcbf --- /dev/null +++ b/gm/collapsepaths.cpp @@ -0,0 +1,161 @@ +/* + * Copyright 2019 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" +#include "include/core/SkCanvas.h" +#include "include/core/SkPath.h" + +namespace { + +void test_collapse1(SkCanvas* canvas, const SkPaint& paint) { + SkPath path; + canvas->translate(0, 0); + path.moveTo( 652.830078125, 673.9365234375); + path.lineTo( 479.50152587890625, 213.412628173828125); + path.lineTo( 511.840545654296875, 209.1551055908203125); + path.lineTo( 528.14959716796875, 208.6212158203125); + path.moveTo( 370.50653076171875, 73.684051513671875); + path.lineTo( 525.02093505859375, 208.6413726806640625); + path.lineTo( 478.403564453125, 213.5998992919921875); + path.setFillType(SkPath::FillType::kEvenOdd_FillType); + canvas->drawPath(path, paint); +} + +void test_collapse2(SkCanvas* canvas, const SkPaint& paint) { + SkPath path; + path.moveTo( 492.781982421875, 508.7139892578125); + path.lineTo( 361.946746826171875, 161.0923004150390625); + path.lineTo( 386.357513427734375, 157.8785552978515625); + path.lineTo( 398.668212890625, 157.475555419921875); + path.moveTo( 279.673004150390625, 55.619640350341796875); + path.lineTo( 396.30657958984375, 157.4907684326171875); + path.lineTo( 361.117950439453125, 161.2336578369140625); + canvas->drawPath(path, paint); +} + +void test_collapse3(SkCanvas* canvas, const SkPaint& paint) { + SkPath path; + path.moveTo(31.9730987548828125, 69.4149169921875); + path.lineTo(36.630767822265625, 67.66190338134765625); + path.lineTo(51.1498870849609375, 64.2765045166015625); + path.moveTo(52.94580078125, 64.05560302734375); + path.lineTo(38.9994354248046875, 66.8980712890625); + path.lineTo(32.229583740234375, 69.31696319580078125); + path.lineTo(12.99810791015625, 22.4723663330078125); + canvas->drawPath(path, paint); +} + +void test_collapse4(SkCanvas* canvas, const SkPaint& paint) { + SkPath path; + path.moveTo( 122.66265869140625, 77.81488800048828125); + path.lineTo( 161.983642578125, 128.557952880859375); + path.lineTo(22.599969863891601562, 76.61859893798828125); + path.lineTo(18.03154754638671875, 76.055633544921875); + path.lineTo(15.40312957763671875, 75.7647247314453125); + path.lineTo(18.572841644287109375, 75.2251129150390625); + path.lineTo(20.895002365112304688, 73.7937774658203125); + canvas->drawPath(path, paint); +} + +void test_collapse5(SkCanvas* canvas, const SkPaint& paint) { + SkPath path; + path.moveTo(52.659847259521484375, 782.0546875); + path.lineTo(136.6915130615234375, 690.18011474609375); + path.lineTo( 392.147796630859375, 554.6090087890625); + path.lineTo( 516.51470947265625, 534.44134521484375); + path.moveTo(154.6182708740234375, 188.230926513671875); + path.lineTo( 430.242095947265625, 546.76605224609375); + path.lineTo( 373.1005859375, 559.0906982421875); + path.setFillType(SkPath::FillType::kEvenOdd_FillType); + canvas->drawPath(path, paint); +} + +void test_collapse6(SkCanvas* canvas, const SkPaint& paint) { + SkPath path; + path.moveTo(13.314494132995605469, 197.7343902587890625); + path.lineTo(34.56102752685546875, 174.5048675537109375); + path.lineTo(99.15048980712890625, 140.22711181640625); + path.lineTo( 130.595367431640625, 135.1279296875); + path.moveTo(39.09362030029296875, 47.59223175048828125); + path.lineTo(108.7822418212890625, 138.244110107421875); + path.lineTo(94.33460235595703125, 141.360260009765625); + path.setFillType(SkPath::FillType::kEvenOdd_FillType); + canvas->drawPath(path, paint); +} + +void test_collapse7(SkCanvas* canvas, const SkPaint& paint) { + SkPath path; + path.moveTo(13.737141609191894531, 204.0111541748046875); + path.lineTo( 35.658111572265625, 180.04425048828125); + path.lineTo(102.2978668212890625, 144.67840576171875); + path.lineTo( 134.74090576171875, 139.4173583984375); + path.moveTo(40.33458709716796875, 49.10297393798828125); + path.lineTo(112.2353668212890625, 142.6324462890625); + path.lineTo(97.32910919189453125, 145.8475189208984375); + path.setFillType(SkPath::FillType::kEvenOdd_FillType); + canvas->drawPath(path, paint); +} + +void test_collapse8(SkCanvas* canvas, const SkPaint& paint) { + SkPath path; + path.moveTo( 11.75, 174.50); + path.lineTo( 30.50, 154.00); + path.lineTo( 87.50, 123.75); + path.lineTo(115.25, 119.25); + path.moveTo( 34.50, 42.00); + path.lineTo( 96.00, 122.00); + path.lineTo( 83.25, 124.75); + path.setFillType(SkPath::FillType::kEvenOdd_FillType); + canvas->drawPath(path, paint); +} + +void test_collapse9(SkCanvas* canvas, const SkPaint& paint) { + SkPath path; + path.moveTo(SkDoubleToScalar( 13.25), SkDoubleToScalar(197.75)); + path.lineTo(SkDoubleToScalar( 34.75), SkDoubleToScalar(174.75)); + path.lineTo(SkDoubleToScalar( 99.0364), SkDoubleToScalar(140.364)); + path.lineTo(SkDoubleToScalar( 99.25), SkDoubleToScalar(140.25)); + path.lineTo(SkDoubleToScalar(100.167), SkDoubleToScalar(140.096)); + path.lineTo(SkDoubleToScalar(130.50), SkDoubleToScalar(135.00)); + path.moveTo(SkDoubleToScalar( 39.25), SkDoubleToScalar( 47.50)); + path.lineTo(SkDoubleToScalar(100.167), SkDoubleToScalar(140.096)); + path.lineTo(SkDoubleToScalar( 99.0364), SkDoubleToScalar(140.364)); + path.lineTo(SkDoubleToScalar( 94.25), SkDoubleToScalar(141.50)); + path.setFillType(SkPath::FillType::kEvenOdd_FillType); + canvas->drawPath(path, paint); +} + +// This one is a thin inverted 'v', but the edges should not disappear at any point. +void test_collapse10(SkCanvas* canvas, const SkPaint& paint) { + SkPath path; + path.moveTo( 5.5, 36.0); + path.lineTo(47.5, 5.0); + path.lineTo(90.0, 36.0); + path.lineTo(88.5, 36.0); + path.lineTo(47.5, 6.0); + path.lineTo( 7.0, 36.0); + canvas->drawPath(path, paint); +} + +}; + +DEF_SIMPLE_GM(collapsepaths, canvas, 500, 600) { + SkPaint paint; + + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kFill_Style); + test_collapse1(canvas, paint); + test_collapse2(canvas, paint); + test_collapse3(canvas, paint); + test_collapse4(canvas, paint); + test_collapse5(canvas, paint); + test_collapse6(canvas, paint); + test_collapse7(canvas, paint); + test_collapse8(canvas, paint); + test_collapse9(canvas, paint); + test_collapse10(canvas, paint); +} diff --git a/gm/crbug_913349.cpp b/gm/crbug_913349.cpp new file mode 100644 index 0000000000..40c8749043 --- /dev/null +++ b/gm/crbug_913349.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2019 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm/gm.h" +#include "include/core/SkCanvas.h" +#include "include/core/SkPaint.h" +#include "include/core/SkPath.h" + +DEF_SIMPLE_GM(crbug_913349, canvas, 500, 600) { + SkPaint paint; + + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kFill_Style); + + // This is a reduction from crbug.com/913349 to 5 verts. + SkPath path; + path.moveTo( 349.5, 225.75); + path.lineTo( 96.5, 74); + path.lineTo( 500.50, 226); + path.lineTo( 350, 226); + path.lineTo( 350, 224); + + canvas->drawPath(path, paint); +} diff --git a/gn/gm.gni b/gn/gm.gni index 02d1f862dd..7348e8b9b3 100644 --- a/gn/gm.gni +++ b/gn/gm.gni @@ -76,6 +76,7 @@ gm_sources = [ "$_gm/clipdrawdraw.cpp", "$_gm/clippedbitmapshaders.cpp", "$_gm/clockwise.cpp", + "$_gm/collapsepaths.cpp", "$_gm/color4f.cpp", "$_gm/coloremoji.cpp", "$_gm/coloremoji_blendmodes.cpp", @@ -107,6 +108,7 @@ gm_sources = [ "$_gm/crbug_892988.cpp", "$_gm/crbug_899512.cpp", "$_gm/crbug_905548.cpp", + "$_gm/crbug_913349.cpp", "$_gm/crbug_918512.cpp", "$_gm/crbug_938592.cpp", "$_gm/crbug_946965.cpp", diff --git a/src/gpu/GrTessellator.cpp b/src/gpu/GrTessellator.cpp index 1ceed08429..0a0cef9114 100644 --- a/src/gpu/GrTessellator.cpp +++ b/src/gpu/GrTessellator.cpp @@ -15,10 +15,11 @@ #include "src/core/SkArenaAlloc.h" #include "src/core/SkGeometry.h" #include "src/core/SkPointPriv.h" -#include "src/core/SkTDPQueue.h" #include #include +#include +#include #include /* @@ -153,6 +154,7 @@ struct Vertex { , fLeftEnclosingEdge(nullptr), fRightEnclosingEdge(nullptr) , fPartner(nullptr) , fAlpha(alpha) + , fSynthetic(false) #if LOGGING_ENABLED , fID (-1.0f) #endif @@ -168,6 +170,7 @@ struct Vertex { Edge* fRightEnclosingEdge; // Nearest edge in the AEL right of this vertex. Vertex* fPartner; // Corresponding inner or outer vertex (for AA). uint8_t fAlpha; + bool fSynthetic; // Is this a synthetic vertex? #if LOGGING_ENABLED float fID; // Identifier used for logging. #endif @@ -352,12 +355,10 @@ struct Edge { , fNextEdgeBelow(nullptr) , fLeftPoly(nullptr) , fRightPoly(nullptr) - , fEvent(nullptr) , fLeftPolyPrev(nullptr) , fLeftPolyNext(nullptr) , fRightPolyPrev(nullptr) , fRightPolyNext(nullptr) - , fOverlap(false) , fUsedInLeftPoly(false) , fUsedInRightPoly(false) , fLine(top, bottom) { @@ -374,12 +375,10 @@ struct Edge { Edge* fNextEdgeBelow; // " Poly* fLeftPoly; // The Poly to the left of this edge, if any. Poly* fRightPoly; // The Poly to the right of this edge, if any. - Event* fEvent; Edge* fLeftPolyPrev; Edge* fLeftPolyNext; Edge* fRightPolyPrev; Edge* fRightPolyNext; - bool fOverlap; // True if there's an overlap region adjacent to this edge. bool fUsedInLeftPoly; bool fUsedInRightPoly; Line fLine; @@ -436,6 +435,28 @@ struct Edge { } }; +struct SSEdge; + +struct SSVertex { + SSVertex(Vertex* v) : fVertex(v), fPrev(nullptr), fNext(nullptr) {} + Vertex* fVertex; + SSEdge* fPrev; + SSEdge* fNext; +}; + +struct SSEdge { + SSEdge(Edge* edge, SSVertex* prev, SSVertex* next) + : fEdge(edge), fEvent(nullptr), fPrev(prev), fNext(next) { + } + Edge* fEdge; + Event* fEvent; + SSVertex* fPrev; + SSVertex* fNext; +}; + +typedef std::unordered_map SSVertexMap; +typedef std::vector SSEdgeList; + struct EdgeList { EdgeList() : fHead(nullptr), fTail(nullptr) {} Edge* fHead; @@ -465,36 +486,70 @@ struct EdgeList { } }; +struct EventList; + struct Event { - Event(Edge* edge, bool isOuterBoundary, const SkPoint& point, uint8_t alpha) - : fEdge(edge), fIsOuterBoundary(isOuterBoundary), fPoint(point), fAlpha(alpha) - , fPrev(nullptr), fNext(nullptr) { + Event(SSEdge* edge, const SkPoint& point, uint8_t alpha) + : fEdge(edge), fPoint(point), fAlpha(alpha) { } - Edge* fEdge; - bool fIsOuterBoundary; + SSEdge* fEdge; SkPoint fPoint; uint8_t fAlpha; - Event* fPrev; - Event* fNext; - void apply(VertexList* mesh, Comparator& c, SkArenaAlloc& alloc); + void apply(VertexList* mesh, Comparator& c, EventList* events, SkArenaAlloc& alloc); }; -bool compare(Event* const& e1, Event* const& e2) { - return e1->fAlpha > e2->fAlpha; -} +struct EventComparator { + enum class Op { kLessThan, kGreaterThan }; + EventComparator(Op op) : fOp(op) {} + bool operator() (Event* const &e1, Event* const &e2) { + return fOp == Op::kLessThan ? e1->fAlpha < e2->fAlpha + : e1->fAlpha > e2->fAlpha; + } + Op fOp; +}; -struct EventList : public SkTDPQueue {}; +typedef std::priority_queue, EventComparator> EventPQ; -void create_event(Edge* e, bool isOuterBoundary, EventList* events, SkArenaAlloc& alloc) { - Edge bisector1(e->fTop, e->fTop->fPartner, 1, Edge::Type::kConnector); - Edge bisector2(e->fBottom, e->fBottom->fPartner, 1, Edge::Type::kConnector); +struct EventList : EventPQ { + EventList(EventComparator comparison) : EventPQ(comparison) { + } +}; + +void create_event(SSEdge* e, EventList* events, SkArenaAlloc& alloc) { + Vertex* prev = e->fPrev->fVertex; + Vertex* next = e->fNext->fVertex; + if (prev == next || !prev->fPartner || !next->fPartner) { + return; + } + Edge bisector1(prev, prev->fPartner, 1, Edge::Type::kConnector); + Edge bisector2(next, next->fPartner, 1, Edge::Type::kConnector); SkPoint p; uint8_t alpha; if (bisector1.intersect(bisector2, &p, &alpha)) { - LOG("found overlap edge %g -> %g, will collapse to %g,%g alpha %d\n", - e->fTop->fID, e->fBottom->fID, p.fX, p.fY, alpha); - e->fEvent = alloc.make(e, isOuterBoundary, p, alpha); - events->insert(e->fEvent); + LOG("found edge event for %g, %g (original %g -> %g), will collapse to %g,%g alpha %d\n", + prev->fID, next->fID, e->fEdge->fTop->fID, e->fEdge->fBottom->fID, p.fX, p.fY, alpha); + e->fEvent = alloc.make(e, p, alpha); + events->push(e->fEvent); + } +} + +void create_event(SSEdge* edge, Vertex* v, SSEdge* other, Vertex* dest, EventList* events, + Comparator& c, SkArenaAlloc& alloc) { + if (!v->fPartner) { + return; + } + Line line = edge->fEdge->fLine; + line.fC = -(dest->fPoint.fX * line.fA + dest->fPoint.fY * line.fB); + Edge bisector(v, v->fPartner, 1, Edge::Type::kConnector); + SkPoint p; + uint8_t alpha = dest->fAlpha; + if (line.intersect(bisector.fLine, &p) && !c.sweep_lt(p, edge->fEdge->fTop->fPoint) && + c.sweep_lt(p, edge->fEdge->fBottom->fPoint)) { + LOG("found p edge event for %g, %g (original %g -> %g), will collapse to %g,%g alpha %d\n", + dest->fID, v->fID, edge->fEdge->fTop->fID, edge->fEdge->fBottom->fID, p.fX, p.fY, + alpha); + edge->fEvent = alloc.make(edge, p, alpha); + events->push(edge->fEvent); } } @@ -1164,6 +1219,7 @@ void merge_vertices(Vertex* src, Vertex* dst, VertexList* mesh, Comparator& c, set_top(edge, dst, nullptr, nullptr, c); } mesh->remove(src); + dst->fSynthetic = true; } Vertex* create_sorted_vertex(const SkPoint& p, uint8_t alpha, VertexList* mesh, @@ -1217,6 +1273,25 @@ SkPoint clamp(SkPoint p, SkPoint min, SkPoint max, Comparator& c) { } } +void compute_bisector(Edge* edge1, Edge* edge2, Vertex* v, SkArenaAlloc& alloc) { + Line line1 = edge1->fLine; + Line line2 = edge2->fLine; + line1.normalize(); + line2.normalize(); + double cosAngle = line1.fA * line2.fA + line1.fB * line2.fB; + if (cosAngle > 0.999) { + return; + } + line1.fC += edge1->fWinding > 0 ? -1 : 1; + line2.fC += edge2->fWinding > 0 ? -1 : 1; + SkPoint p; + if (line1.intersect(line2, &p)) { + uint8_t alpha = edge1->fType == Edge::Type::kOuter ? 255 : 0; + v->fPartner = alloc.make(p, alpha); + LOG("computed bisector (%g,%g) alpha %d for vertex %g\n", p.fX, p.fY, alpha, v->fID); + } +} + bool check_for_intersection(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current, VertexList* mesh, Comparator& c, SkArenaAlloc& alloc) { if (!left || !right) { @@ -1250,17 +1325,8 @@ bool check_for_intersection(Edge* left, Edge* right, EdgeList* activeEdges, Vert } else { v = create_sorted_vertex(p, alpha, mesh, top, c, alloc); if (left->fTop->fPartner) { - Line line1 = left->fLine; - Line line2 = right->fLine; - int dir = left->fType == Edge::Type::kOuter ? -1 : 1; - line1.fC += sqrt(left->fLine.magSq()) * (left->fWinding > 0 ? 1 : -1) * dir; - line2.fC += sqrt(right->fLine.magSq()) * (right->fWinding > 0 ? 1 : -1) * dir; - SkPoint p; - if (line1.intersect(line2, &p)) { - LOG("synthesizing partner (%g,%g) for intersection vertex %g\n", - p.fX, p.fY, v->fID); - v->fPartner = alloc.make(p, 255 - v->fAlpha); - } + v->fSynthetic = true; + compute_bisector(left, right, v, alloc); } } rewind(activeEdges, current, top ? top : v, c); @@ -1433,6 +1499,21 @@ void dump_mesh(const VertexList& mesh) { #endif } +void dump_skel(const SSEdgeList& ssEdges) { +#if LOGGING_ENABLED + LOG("skeleton:\n"); + for (SSEdge* edge : ssEdges) { + if (edge->fEdge) { + LOG("skel edge %g -> %g (original %g -> %g)\n", + edge->fPrev->fVertex->fID, + edge->fNext->fVertex->fID, + edge->fEdge->fTop->fID, + edge->fEdge->fBottom->fID); + } + } +#endif +} + #ifdef SK_DEBUG void validate_edge_pair(Edge* left, Edge* right, Comparator& c) { if (!left || !right) { @@ -1470,12 +1551,16 @@ void validate_edge_list(EdgeList* edges, Comparator& c) { // Stage 4: Simplify the mesh by inserting new vertices at intersecting edges. +bool connected(Vertex* v) { + return v->fFirstEdgeAbove || v->fFirstEdgeBelow; +} + bool simplify(VertexList* mesh, Comparator& c, SkArenaAlloc& alloc) { LOG("simplifying complex polygons\n"); EdgeList activeEdges; bool found = false; for (Vertex* v = mesh->fHead; v != nullptr; v = v->fNext) { - if (!v->fFirstEdgeAbove && !v->fFirstEdgeBelow) { + if (!connected(v)) { continue; } Edge* leftEnclosingEdge; @@ -1532,7 +1617,7 @@ Poly* tessellate(const VertexList& vertices, SkArenaAlloc& alloc) { EdgeList activeEdges; Poly* polys = nullptr; for (Vertex* v = vertices.fHead; v != nullptr; v = v->fNext) { - if (!v->fFirstEdgeAbove && !v->fFirstEdgeBelow) { + if (!connected(v)) { continue; } #if LOGGING_ENABLED @@ -1639,7 +1724,7 @@ void remove_non_boundary_edges(const VertexList& mesh, SkPath::FillType fillType LOG("removing non-boundary edges\n"); EdgeList activeEdges; for (Vertex* v = mesh.fHead; v != nullptr; v = v->fNext) { - if (!v->fFirstEdgeAbove && !v->fFirstEdgeBelow) { + if (!connected(v)) { continue; } Edge* leftEnclosingEdge; @@ -1674,31 +1759,6 @@ void get_edge_normal(const Edge* e, SkVector* normal) { SkDoubleToScalar(e->fLine.fB)); } -void reconnect(Edge* edge, Vertex* src, Vertex* dst, Comparator& c) { - disconnect(edge); - if (src == edge->fTop) { - edge->fTop = dst; - } else { - SkASSERT(src == edge->fBottom); - edge->fBottom = dst; - } - if (edge->fEvent) { - edge->fEvent->fEdge = nullptr; - } - if (edge->fTop == edge->fBottom) { - return; - } - if (c.sweep_lt(edge->fBottom->fPoint, edge->fTop->fPoint)) { - using std::swap; - swap(edge->fTop, edge->fBottom); - edge->fWinding *= -1; - } - edge->recompute(); - insert_edge_below(edge, edge->fTop, c); - insert_edge_above(edge, edge->fBottom, c); - merge_collinear_edges(edge, nullptr, nullptr, c); -} - // Stage 5c: detect and remove "pointy" vertices whose edge normals point in opposite directions // and whose adjacent vertices are less than a quarter pixel from an edge. These are guaranteed to // invert on stroking. @@ -1715,7 +1775,15 @@ void simplify_boundary(EdgeList* boundary, Comparator& c, SkArenaAlloc& alloc) { SkVector normal; get_edge_normal(e, &normal); constexpr double kQuarterPixelSq = 0.25f * 0.25f; - if (prev != next && prevNormal.dot(normal) < 0.0 && + if (prev == next) { + remove_edge(prevEdge, boundary); + remove_edge(e, boundary); + prevEdge = boundary->fTail; + e = boundary->fHead; + if (prevEdge) { + get_edge_normal(prevEdge, &prevNormal); + } + } else if (prevNormal.dot(normal) < 0.0 && (distPrev * distPrev <= kQuarterPixelSq || distNext * distNext <= kQuarterPixelSq)) { Edge* join = new_edge(prev, next, Edge::Type::kInner, c, alloc); if (prev->fPoint != next->fPoint) { @@ -1741,56 +1809,69 @@ void simplify_boundary(EdgeList* boundary, Comparator& c, SkArenaAlloc& alloc) { } } -void reconnect_all_overlap_edges(Vertex* src, Vertex* dst, Edge* current, Comparator& c) { - if (src->fPartner) { - src->fPartner->fPartner = dst; +void ss_connect(Vertex* v, Vertex* dest, Comparator& c, SkArenaAlloc& alloc) { + if (v == dest) { + return; } - for (Edge* e = src->fFirstEdgeAbove; e; ) { - Edge* next = e->fNextEdgeAbove; - if (e->fOverlap && e != current) { - reconnect(e, src, dst, c); - } - e = next; - } - for (Edge* e = src->fFirstEdgeBelow; e; ) { - Edge* next = e->fNextEdgeBelow; - if (e->fOverlap && e != current) { - reconnect(e, src, dst, c); - } - e = next; + LOG("ss_connecting vertex %g to vertex %g\n", v->fID, dest->fID); + if (v->fSynthetic) { + connect(v, dest, Edge::Type::kConnector, c, alloc, 0); + } else if (v->fPartner) { + LOG("setting %g's partner to %g ", v->fPartner->fID, dest->fID); + LOG("and %g's partner to null\n", v->fID); + v->fPartner->fPartner = dest; + v->fPartner = nullptr; } } -void Event::apply(VertexList* mesh, Comparator& c, SkArenaAlloc& alloc) { - if (!fEdge || !fEdge->fTop || !fEdge->fBottom) { +void Event::apply(VertexList* mesh, Comparator& c, EventList* events, SkArenaAlloc& alloc) { + if (!fEdge) { return; } - Vertex* top = fEdge->fTop; - Vertex* bottom = fEdge->fBottom; - Vertex* dest = create_sorted_vertex(fPoint, fAlpha, mesh, fEdge->fTop, c, alloc); - LOG("collapsing edge %g -> %g to %g (%g, %g) alpha %d\n", - top->fID, bottom->fID, dest->fID, fPoint.fX, fPoint.fY, fAlpha); - reconnect_all_overlap_edges(top, dest, fEdge, c); - reconnect_all_overlap_edges(bottom, dest, fEdge, c); - - // Since the destination has multiple partners, give it none. - dest->fPartner = nullptr; - - // Disconnect all collapsed edges except outer boundaries. - // Those are required to preserve shape coverage and winding correctness. - if (!fIsOuterBoundary) { - disconnect(fEdge); - } else { - LOG("edge %g -> %g is outer boundary; not disconnecting.\n", - fEdge->fTop->fID, fEdge->fBottom->fID); - fEdge->fWinding = fEdge->fWinding >= 0 ? 1 : -1; + Vertex* prev = fEdge->fPrev->fVertex; + Vertex* next = fEdge->fNext->fVertex; + SSEdge* prevEdge = fEdge->fPrev->fPrev; + SSEdge* nextEdge = fEdge->fNext->fNext; + if (!prevEdge || !nextEdge || !prevEdge->fEdge || !nextEdge->fEdge) { + return; } + Vertex* dest = create_sorted_vertex(fPoint, fAlpha, mesh, prev, c, alloc); + dest->fSynthetic = true; + SSVertex* ssv = alloc.make(dest); + LOG("collapsing %g, %g (original edge %g -> %g) to %g (%g, %g) alpha %d\n", + prev->fID, next->fID, fEdge->fEdge->fTop->fID, fEdge->fEdge->fBottom->fID, + dest->fID, fPoint.fX, fPoint.fY, fAlpha); + fEdge->fEdge = nullptr; - // If top still has some connected edges, set its partner to dest. - top->fPartner = top->fFirstEdgeAbove || top->fFirstEdgeBelow ? dest : nullptr; + ss_connect(prev, dest, c, alloc); + ss_connect(next, dest, c, alloc); - // If bottom still has some connected edges, set its partner to dest. - bottom->fPartner = bottom->fFirstEdgeAbove || bottom->fFirstEdgeBelow ? dest : nullptr; + prevEdge->fNext = nextEdge->fPrev = ssv; + ssv->fPrev = prevEdge; + ssv->fNext = nextEdge; + if (!prevEdge->fEdge || !nextEdge->fEdge) { + return; + } + if (prevEdge->fEvent) { + prevEdge->fEvent->fEdge = nullptr; + } + if (nextEdge->fEvent) { + nextEdge->fEvent->fEdge = nullptr; + } + if (prevEdge->fPrev == nextEdge->fNext) { + ss_connect(prevEdge->fPrev->fVertex, dest, c, alloc); + prevEdge->fEdge = nextEdge->fEdge = nullptr; + } else { + compute_bisector(prevEdge->fEdge, nextEdge->fEdge, dest, alloc); + SkASSERT(prevEdge != fEdge && nextEdge != fEdge); + if (dest->fPartner) { + create_event(prevEdge, events, alloc); + create_event(nextEdge, events, alloc); + } else { + create_event(prevEdge, prevEdge->fPrev->fVertex, nextEdge, dest, events, c, alloc); + create_event(nextEdge, nextEdge->fNext->fVertex, prevEdge, dest, events, c, alloc); + } + } } bool is_overlap_edge(Edge* e) { @@ -1805,51 +1886,82 @@ bool is_overlap_edge(Edge* e) { // This is a stripped-down version of tessellate() which computes edges which // join two filled regions, which represent overlap regions, and collapses them. -bool collapse_overlap_regions(VertexList* mesh, Comparator& c, SkArenaAlloc& alloc) { +bool collapse_overlap_regions(VertexList* mesh, Comparator& c, SkArenaAlloc& alloc, + EventComparator comp) { LOG("\nfinding overlap regions\n"); EdgeList activeEdges; - EventList events; + EventList events(comp); + SSVertexMap ssVertices; + SSEdgeList ssEdges; for (Vertex* v = mesh->fHead; v != nullptr; v = v->fNext) { - if (!v->fFirstEdgeAbove && !v->fFirstEdgeBelow) { + if (!connected(v)) { continue; } Edge* leftEnclosingEdge; Edge* rightEnclosingEdge; find_enclosing_edges(v, &activeEdges, &leftEnclosingEdge, &rightEnclosingEdge); - for (Edge* e = v->fLastEdgeAbove; e; e = e->fPrevEdgeAbove) { + for (Edge* e = v->fLastEdgeAbove; e && e != leftEnclosingEdge;) { Edge* prev = e->fPrevEdgeAbove ? e->fPrevEdgeAbove : leftEnclosingEdge; remove_edge(e, &activeEdges); + bool leftOverlap = prev && is_overlap_edge(prev); + bool rightOverlap = is_overlap_edge(e); + bool isOuterBoundary = e->fType == Edge::Type::kOuter && + (!prev || prev->fWinding == 0 || e->fWinding == 0); if (prev) { e->fWinding -= prev->fWinding; } + if (leftOverlap && rightOverlap) { + LOG("found interior overlap edge %g -> %g, disconnecting\n", + e->fTop->fID, e->fBottom->fID); + disconnect(e); + } else if (leftOverlap || rightOverlap) { + LOG("found overlap edge %g -> %g%s\n", e->fTop->fID, e->fBottom->fID, + isOuterBoundary ? ", is outer boundary" : ""); + Vertex* prevVertex = e->fWinding < 0 ? e->fBottom : e->fTop; + Vertex* nextVertex = e->fWinding < 0 ? e->fTop : e->fBottom; + SSVertex* ssPrev = ssVertices[prevVertex]; + if (!ssPrev) { + ssPrev = ssVertices[prevVertex] = alloc.make(prevVertex); + } + SSVertex* ssNext = ssVertices[nextVertex]; + if (!ssNext) { + ssNext = ssVertices[nextVertex] = alloc.make(nextVertex); + } + SSEdge* ssEdge = alloc.make(e, ssPrev, ssNext); + ssEdges.push_back(ssEdge); +// SkASSERT(!ssPrev->fNext && !ssNext->fPrev); + ssPrev->fNext = ssNext->fPrev = ssEdge; + create_event(ssEdge, &events, alloc); + if (!isOuterBoundary) { + disconnect(e); + } + } + e = prev; } Edge* prev = leftEnclosingEdge; for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) { if (prev) { e->fWinding += prev->fWinding; - e->fOverlap = e->fOverlap || is_overlap_edge(prev); - } - e->fOverlap = e->fOverlap || is_overlap_edge(e); - if (e->fOverlap) { - // If this edge borders a zero-winding area, it's a boundary; don't disconnect it. - bool isOuterBoundary = e->fType == Edge::Type::kOuter && - (!prev || prev->fWinding == 0 || e->fWinding == 0); - create_event(e, isOuterBoundary, &events, alloc); } insert_edge(e, prev, &activeEdges); prev = e; } } + bool complex = events.size() > 0; + LOG("\ncollapsing overlap regions\n"); - if (events.count() == 0) { - return false; - } - while (events.count() > 0) { - Event* event = events.peek(); + while (events.size() > 0) { + Event* event = events.top(); events.pop(); - event->apply(mesh, c, alloc); + event->apply(mesh, c, &events, alloc); } - return true; + dump_skel(ssEdges); + for (SSEdge* edge : ssEdges) { + if (Edge* e = edge->fEdge) { + connect(edge->fPrev->fVertex, edge->fNext->fVertex, e->fType, c, alloc, 0); + } + } + return complex; } bool inversion(Vertex* prev, Vertex* next, Edge* origEdge, Comparator& c) { @@ -2118,6 +2230,8 @@ Poly* contours_to_polys(VertexList* contours, int contourCnt, SkPath::FillType f sort_mesh(&mesh, c, alloc); merge_coincident_vertices(&mesh, c, alloc); simplify(&mesh, c, alloc); + LOG("\nsimplified mesh:\n"); + dump_mesh(mesh); if (antialias) { VertexList innerMesh; extract_boundaries(mesh, &innerMesh, outerMesh, fillType, c, alloc); @@ -2131,8 +2245,10 @@ Poly* contours_to_polys(VertexList* contours, int contourCnt, SkPath::FillType f dump_mesh(innerMesh); LOG("\nouter mesh before:\n"); dump_mesh(*outerMesh); - was_complex = collapse_overlap_regions(&innerMesh, c, alloc) || was_complex; - was_complex = collapse_overlap_regions(outerMesh, c, alloc) || was_complex; + EventComparator eventLT(EventComparator::Op::kLessThan); + EventComparator eventGT(EventComparator::Op::kGreaterThan); + was_complex = collapse_overlap_regions(&innerMesh, c, alloc, eventLT) || was_complex; + was_complex = collapse_overlap_regions(outerMesh, c, alloc, eventGT) || was_complex; if (was_complex) { LOG("found complex mesh; taking slow path\n"); VertexList aaMesh; @@ -2145,6 +2261,7 @@ Poly* contours_to_polys(VertexList* contours, int contourCnt, SkPath::FillType f sorted_merge(&innerMesh, outerMesh, &aaMesh, c); merge_coincident_vertices(&aaMesh, c, alloc); simplify(&aaMesh, c, alloc); + LOG("combined and simplified mesh:\n"); dump_mesh(aaMesh); outerMesh->fHead = outerMesh->fTail = nullptr; return tessellate(aaMesh, alloc);