Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2020 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "src/gpu/GrAATriangulator.h" |
| 9 | |
Chris Dalton | d538479 | 2021-01-20 15:43:24 -0700 | [diff] [blame] | 10 | #include "src/gpu/GrEagerVertexAllocator.h" |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 11 | #include <queue> |
| 12 | #include <vector> |
| 13 | #include <unordered_map> |
| 14 | |
| 15 | #if TRIANGULATOR_LOGGING |
| 16 | #define TESS_LOG SkDebugf |
| 17 | #define DUMP_MESH(MESH) (MESH).dump() |
| 18 | #else |
| 19 | #define TESS_LOG(...) |
| 20 | #define DUMP_MESH(MESH) |
| 21 | #endif |
| 22 | |
| 23 | constexpr static float kCosMiterAngle = 0.97f; // Corresponds to an angle of ~14 degrees. |
| 24 | |
| 25 | using EdgeType = GrTriangulator::EdgeType; |
| 26 | using Vertex = GrTriangulator::Vertex; |
| 27 | using VertexList = GrTriangulator::VertexList; |
| 28 | using Line = GrTriangulator::Line; |
| 29 | using Edge = GrTriangulator::Edge; |
| 30 | using EdgeList = GrTriangulator::EdgeList; |
| 31 | using Poly = GrTriangulator::Poly; |
| 32 | using Comparator = GrTriangulator::Comparator; |
| 33 | using SSEdge = GrAATriangulator::SSEdge; |
| 34 | using EventList = GrAATriangulator::EventList; |
| 35 | using Event = GrAATriangulator::Event; |
| 36 | using EventComparator = GrAATriangulator::EventComparator; |
| 37 | |
| 38 | struct SSVertex { |
| 39 | SSVertex(Vertex* v) : fVertex(v), fPrev(nullptr), fNext(nullptr) {} |
| 40 | Vertex* fVertex; |
| 41 | SSEdge* fPrev; |
| 42 | SSEdge* fNext; |
| 43 | }; |
| 44 | |
| 45 | struct GrAATriangulator::SSEdge { |
| 46 | SSEdge(Edge* edge, SSVertex* prev, SSVertex* next) |
| 47 | : fEdge(edge), fEvent(nullptr), fPrev(prev), fNext(next) { |
| 48 | } |
| 49 | Edge* fEdge; |
| 50 | Event* fEvent; |
| 51 | SSVertex* fPrev; |
| 52 | SSVertex* fNext; |
| 53 | }; |
| 54 | |
| 55 | typedef std::unordered_map<Vertex*, SSVertex*> SSVertexMap; |
| 56 | typedef std::vector<SSEdge*> SSEdgeList; |
| 57 | typedef std::priority_queue<Event*, std::vector<Event*>, EventComparator> EventPQ; |
| 58 | |
| 59 | struct GrAATriangulator::EventList : EventPQ { |
| 60 | EventList(EventComparator comparison) : EventPQ(comparison) { |
| 61 | } |
| 62 | }; |
| 63 | |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 64 | void GrAATriangulator::makeEvent(SSEdge* e, EventList* events) const { |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 65 | Vertex* prev = e->fPrev->fVertex; |
| 66 | Vertex* next = e->fNext->fVertex; |
| 67 | if (prev == next || !prev->fPartner || !next->fPartner) { |
| 68 | return; |
| 69 | } |
| 70 | Edge bisector1(prev, prev->fPartner, 1, EdgeType::kConnector); |
| 71 | Edge bisector2(next, next->fPartner, 1, EdgeType::kConnector); |
| 72 | SkPoint p; |
| 73 | uint8_t alpha; |
| 74 | if (bisector1.intersect(bisector2, &p, &alpha)) { |
| 75 | TESS_LOG("found edge event for %g, %g (original %g -> %g), " |
| 76 | "will collapse to %g,%g alpha %d\n", |
| 77 | prev->fID, next->fID, e->fEdge->fTop->fID, e->fEdge->fBottom->fID, p.fX, p.fY, |
| 78 | alpha); |
Chris Dalton | 8a42b09 | 2021-01-21 22:09:26 -0700 | [diff] [blame] | 79 | e->fEvent = fAlloc->make<Event>(e, p, alpha); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 80 | events->push(e->fEvent); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | void GrAATriangulator::makeEvent(SSEdge* edge, Vertex* v, SSEdge* other, Vertex* dest, |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 85 | EventList* events, const Comparator& c) const { |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 86 | if (!v->fPartner) { |
| 87 | return; |
| 88 | } |
| 89 | Vertex* top = edge->fEdge->fTop; |
| 90 | Vertex* bottom = edge->fEdge->fBottom; |
| 91 | if (!top || !bottom ) { |
| 92 | return; |
| 93 | } |
| 94 | Line line = edge->fEdge->fLine; |
| 95 | line.fC = -(dest->fPoint.fX * line.fA + dest->fPoint.fY * line.fB); |
| 96 | Edge bisector(v, v->fPartner, 1, EdgeType::kConnector); |
| 97 | SkPoint p; |
| 98 | uint8_t alpha = dest->fAlpha; |
| 99 | if (line.intersect(bisector.fLine, &p) && !c.sweep_lt(p, top->fPoint) && |
| 100 | c.sweep_lt(p, bottom->fPoint)) { |
| 101 | TESS_LOG("found p edge event for %g, %g (original %g -> %g), " |
| 102 | "will collapse to %g,%g alpha %d\n", |
| 103 | dest->fID, v->fID, top->fID, bottom->fID, p.fX, p.fY, alpha); |
Chris Dalton | 8a42b09 | 2021-01-21 22:09:26 -0700 | [diff] [blame] | 104 | edge->fEvent = fAlloc->make<Event>(edge, p, alpha); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 105 | events->push(edge->fEvent); |
| 106 | } |
| 107 | } |
| 108 | |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 109 | void GrAATriangulator::connectPartners(VertexList* mesh, const Comparator& c) const { |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 110 | for (Vertex* outer = mesh->fHead; outer; outer = outer->fNext) { |
| 111 | if (Vertex* inner = outer->fPartner) { |
| 112 | if ((inner->fPrev || inner->fNext) && (outer->fPrev || outer->fNext)) { |
| 113 | // Connector edges get zero winding, since they're only structural (i.e., to ensure |
| 114 | // no 0-0-0 alpha triangles are produced), and shouldn't affect the poly winding |
| 115 | // number. |
Chris Dalton | 0879da0 | 2021-01-29 13:57:25 -0700 | [diff] [blame] | 116 | this->makeConnectingEdge(outer, inner, EdgeType::kConnector, c, 0); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 117 | inner->fPartner = outer->fPartner = nullptr; |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | static void dump_skel(const SSEdgeList& ssEdges) { |
| 124 | #if TRIANGULATOR_LOGGING |
| 125 | for (SSEdge* edge : ssEdges) { |
| 126 | if (edge->fEdge) { |
| 127 | TESS_LOG("skel edge %g -> %g", |
| 128 | edge->fPrev->fVertex->fID, |
| 129 | edge->fNext->fVertex->fID); |
| 130 | if (edge->fEdge->fTop && edge->fEdge->fBottom) { |
| 131 | TESS_LOG(" (original %g -> %g)\n", |
| 132 | edge->fEdge->fTop->fID, |
| 133 | edge->fEdge->fBottom->fID); |
| 134 | } else { |
| 135 | TESS_LOG("\n"); |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | #endif |
| 140 | } |
| 141 | |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 142 | void GrAATriangulator::removeNonBoundaryEdges(const VertexList& mesh) const { |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 143 | TESS_LOG("removing non-boundary edges\n"); |
| 144 | EdgeList activeEdges; |
| 145 | for (Vertex* v = mesh.fHead; v != nullptr; v = v->fNext) { |
| 146 | if (!v->isConnected()) { |
| 147 | continue; |
| 148 | } |
| 149 | Edge* leftEnclosingEdge; |
| 150 | Edge* rightEnclosingEdge; |
| 151 | FindEnclosingEdges(v, &activeEdges, &leftEnclosingEdge, &rightEnclosingEdge); |
| 152 | bool prevFilled = leftEnclosingEdge && this->applyFillType(leftEnclosingEdge->fWinding); |
| 153 | for (Edge* e = v->fFirstEdgeAbove; e;) { |
| 154 | Edge* next = e->fNextEdgeAbove; |
| 155 | activeEdges.remove(e); |
| 156 | bool filled = this->applyFillType(e->fWinding); |
| 157 | if (filled == prevFilled) { |
| 158 | e->disconnect(); |
| 159 | } |
| 160 | prevFilled = filled; |
| 161 | e = next; |
| 162 | } |
| 163 | Edge* prev = leftEnclosingEdge; |
| 164 | for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) { |
| 165 | if (prev) { |
| 166 | e->fWinding += prev->fWinding; |
| 167 | } |
| 168 | activeEdges.insert(e, prev); |
| 169 | prev = e; |
| 170 | } |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | // Note: this is the normal to the edge, but not necessarily unit length. |
| 175 | static void get_edge_normal(const Edge* e, SkVector* normal) { |
| 176 | normal->set(SkDoubleToScalar(e->fLine.fA), |
| 177 | SkDoubleToScalar(e->fLine.fB)); |
| 178 | } |
| 179 | |
| 180 | // Stage 5c: detect and remove "pointy" vertices whose edge normals point in opposite directions |
| 181 | // and whose adjacent vertices are less than a quarter pixel from an edge. These are guaranteed to |
| 182 | // invert on stroking. |
| 183 | |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 184 | void GrAATriangulator::simplifyBoundary(EdgeList* boundary, const Comparator& c) const { |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 185 | Edge* prevEdge = boundary->fTail; |
| 186 | SkVector prevNormal; |
| 187 | get_edge_normal(prevEdge, &prevNormal); |
| 188 | for (Edge* e = boundary->fHead; e != nullptr;) { |
| 189 | Vertex* prev = prevEdge->fWinding == 1 ? prevEdge->fTop : prevEdge->fBottom; |
| 190 | Vertex* next = e->fWinding == 1 ? e->fBottom : e->fTop; |
| 191 | double distPrev = e->dist(prev->fPoint); |
| 192 | double distNext = prevEdge->dist(next->fPoint); |
| 193 | SkVector normal; |
| 194 | get_edge_normal(e, &normal); |
| 195 | constexpr double kQuarterPixelSq = 0.25f * 0.25f; |
| 196 | if (prev == next) { |
| 197 | boundary->remove(prevEdge); |
| 198 | boundary->remove(e); |
| 199 | prevEdge = boundary->fTail; |
| 200 | e = boundary->fHead; |
| 201 | if (prevEdge) { |
| 202 | get_edge_normal(prevEdge, &prevNormal); |
| 203 | } |
| 204 | } else if (prevNormal.dot(normal) < 0.0 && |
| 205 | (distPrev * distPrev <= kQuarterPixelSq || distNext * distNext <= kQuarterPixelSq)) { |
| 206 | Edge* join = this->makeEdge(prev, next, EdgeType::kInner, c); |
| 207 | if (prev->fPoint != next->fPoint) { |
| 208 | join->fLine.normalize(); |
| 209 | join->fLine = join->fLine * join->fWinding; |
| 210 | } |
| 211 | boundary->insert(join, e); |
| 212 | boundary->remove(prevEdge); |
| 213 | boundary->remove(e); |
| 214 | if (join->fLeft && join->fRight) { |
| 215 | prevEdge = join->fLeft; |
| 216 | e = join; |
| 217 | } else { |
| 218 | prevEdge = boundary->fTail; |
| 219 | e = boundary->fHead; // join->fLeft ? join->fLeft : join; |
| 220 | } |
| 221 | get_edge_normal(prevEdge, &prevNormal); |
| 222 | } else { |
| 223 | prevEdge = e; |
| 224 | prevNormal = normal; |
| 225 | e = e->fRight; |
| 226 | } |
| 227 | } |
| 228 | } |
| 229 | |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 230 | void GrAATriangulator::connectSSEdge(Vertex* v, Vertex* dest, const Comparator& c) const { |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 231 | if (v == dest) { |
| 232 | return; |
| 233 | } |
| 234 | TESS_LOG("ss_connecting vertex %g to vertex %g\n", v->fID, dest->fID); |
| 235 | if (v->fSynthetic) { |
Chris Dalton | 0879da0 | 2021-01-29 13:57:25 -0700 | [diff] [blame] | 236 | this->makeConnectingEdge(v, dest, EdgeType::kConnector, c, 0); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 237 | } else if (v->fPartner) { |
| 238 | TESS_LOG("setting %g's partner to %g ", v->fPartner->fID, dest->fID); |
| 239 | TESS_LOG("and %g's partner to null\n", v->fID); |
| 240 | v->fPartner->fPartner = dest; |
| 241 | v->fPartner = nullptr; |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | void GrAATriangulator::Event::apply(VertexList* mesh, const Comparator& c, EventList* events, |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 246 | const GrAATriangulator* triangulator) { |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 247 | if (!fEdge) { |
| 248 | return; |
| 249 | } |
| 250 | Vertex* prev = fEdge->fPrev->fVertex; |
| 251 | Vertex* next = fEdge->fNext->fVertex; |
| 252 | SSEdge* prevEdge = fEdge->fPrev->fPrev; |
| 253 | SSEdge* nextEdge = fEdge->fNext->fNext; |
| 254 | if (!prevEdge || !nextEdge || !prevEdge->fEdge || !nextEdge->fEdge) { |
| 255 | return; |
| 256 | } |
| 257 | Vertex* dest = triangulator->makeSortedVertex(fPoint, fAlpha, mesh, prev, c); |
| 258 | dest->fSynthetic = true; |
Chris Dalton | 8a42b09 | 2021-01-21 22:09:26 -0700 | [diff] [blame] | 259 | SSVertex* ssv = triangulator->fAlloc->make<SSVertex>(dest); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 260 | TESS_LOG("collapsing %g, %g (original edge %g -> %g) to %g (%g, %g) alpha %d\n", |
| 261 | prev->fID, next->fID, fEdge->fEdge->fTop->fID, fEdge->fEdge->fBottom->fID, dest->fID, |
| 262 | fPoint.fX, fPoint.fY, fAlpha); |
| 263 | fEdge->fEdge = nullptr; |
| 264 | |
| 265 | triangulator->connectSSEdge(prev, dest, c); |
| 266 | triangulator->connectSSEdge(next, dest, c); |
| 267 | |
| 268 | prevEdge->fNext = nextEdge->fPrev = ssv; |
| 269 | ssv->fPrev = prevEdge; |
| 270 | ssv->fNext = nextEdge; |
| 271 | if (!prevEdge->fEdge || !nextEdge->fEdge) { |
| 272 | return; |
| 273 | } |
| 274 | if (prevEdge->fEvent) { |
| 275 | prevEdge->fEvent->fEdge = nullptr; |
| 276 | } |
| 277 | if (nextEdge->fEvent) { |
| 278 | nextEdge->fEvent->fEdge = nullptr; |
| 279 | } |
| 280 | if (prevEdge->fPrev == nextEdge->fNext) { |
| 281 | triangulator->connectSSEdge(prevEdge->fPrev->fVertex, dest, c); |
| 282 | prevEdge->fEdge = nextEdge->fEdge = nullptr; |
| 283 | } else { |
| 284 | triangulator->computeBisector(prevEdge->fEdge, nextEdge->fEdge, dest); |
| 285 | SkASSERT(prevEdge != fEdge && nextEdge != fEdge); |
| 286 | if (dest->fPartner) { |
| 287 | triangulator->makeEvent(prevEdge, events); |
| 288 | triangulator->makeEvent(nextEdge, events); |
| 289 | } else { |
| 290 | triangulator->makeEvent(prevEdge, prevEdge->fPrev->fVertex, nextEdge, dest, events, c); |
| 291 | triangulator->makeEvent(nextEdge, nextEdge->fNext->fVertex, prevEdge, dest, events, c); |
| 292 | } |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | static bool is_overlap_edge(Edge* e) { |
| 297 | if (e->fType == EdgeType::kOuter) { |
| 298 | return e->fWinding != 0 && e->fWinding != 1; |
| 299 | } else if (e->fType == EdgeType::kInner) { |
| 300 | return e->fWinding != 0 && e->fWinding != -2; |
| 301 | } else { |
| 302 | return false; |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | // This is a stripped-down version of tessellate() which computes edges which |
| 307 | // join two filled regions, which represent overlap regions, and collapses them. |
| 308 | bool GrAATriangulator::collapseOverlapRegions(VertexList* mesh, const Comparator& c, |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 309 | EventComparator comp) const { |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 310 | TESS_LOG("\nfinding overlap regions\n"); |
| 311 | EdgeList activeEdges; |
| 312 | EventList events(comp); |
| 313 | SSVertexMap ssVertices; |
| 314 | SSEdgeList ssEdges; |
| 315 | for (Vertex* v = mesh->fHead; v != nullptr; v = v->fNext) { |
| 316 | if (!v->isConnected()) { |
| 317 | continue; |
| 318 | } |
| 319 | Edge* leftEnclosingEdge; |
| 320 | Edge* rightEnclosingEdge; |
| 321 | FindEnclosingEdges(v, &activeEdges, &leftEnclosingEdge, &rightEnclosingEdge); |
| 322 | for (Edge* e = v->fLastEdgeAbove; e && e != leftEnclosingEdge;) { |
| 323 | Edge* prev = e->fPrevEdgeAbove ? e->fPrevEdgeAbove : leftEnclosingEdge; |
| 324 | activeEdges.remove(e); |
| 325 | bool leftOverlap = prev && is_overlap_edge(prev); |
| 326 | bool rightOverlap = is_overlap_edge(e); |
| 327 | bool isOuterBoundary = e->fType == EdgeType::kOuter && |
| 328 | (!prev || prev->fWinding == 0 || e->fWinding == 0); |
| 329 | if (prev) { |
| 330 | e->fWinding -= prev->fWinding; |
| 331 | } |
| 332 | if (leftOverlap && rightOverlap) { |
| 333 | TESS_LOG("found interior overlap edge %g -> %g, disconnecting\n", |
| 334 | e->fTop->fID, e->fBottom->fID); |
| 335 | e->disconnect(); |
| 336 | } else if (leftOverlap || rightOverlap) { |
| 337 | TESS_LOG("found overlap edge %g -> %g%s\n", |
| 338 | e->fTop->fID, e->fBottom->fID, |
| 339 | isOuterBoundary ? ", is outer boundary" : ""); |
| 340 | Vertex* prevVertex = e->fWinding < 0 ? e->fBottom : e->fTop; |
| 341 | Vertex* nextVertex = e->fWinding < 0 ? e->fTop : e->fBottom; |
| 342 | SSVertex* ssPrev = ssVertices[prevVertex]; |
| 343 | if (!ssPrev) { |
Chris Dalton | 8a42b09 | 2021-01-21 22:09:26 -0700 | [diff] [blame] | 344 | ssPrev = ssVertices[prevVertex] = fAlloc->make<SSVertex>(prevVertex); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 345 | } |
| 346 | SSVertex* ssNext = ssVertices[nextVertex]; |
| 347 | if (!ssNext) { |
Chris Dalton | 8a42b09 | 2021-01-21 22:09:26 -0700 | [diff] [blame] | 348 | ssNext = ssVertices[nextVertex] = fAlloc->make<SSVertex>(nextVertex); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 349 | } |
Chris Dalton | 8a42b09 | 2021-01-21 22:09:26 -0700 | [diff] [blame] | 350 | SSEdge* ssEdge = fAlloc->make<SSEdge>(e, ssPrev, ssNext); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 351 | ssEdges.push_back(ssEdge); |
| 352 | // SkASSERT(!ssPrev->fNext && !ssNext->fPrev); |
| 353 | ssPrev->fNext = ssNext->fPrev = ssEdge; |
| 354 | this->makeEvent(ssEdge, &events); |
| 355 | if (!isOuterBoundary) { |
| 356 | e->disconnect(); |
Michael Ludwig | 70fae35 | 2021-04-15 12:42:23 -0400 | [diff] [blame] | 357 | } else { |
| 358 | SkASSERT(e->fType != EdgeType::kConnector); |
| 359 | // Ensure winding values match expected scale for the edge type. During merging of |
| 360 | // collinear edges in overlap regions, windings are summed and so could exceed the |
| 361 | // expected +/-1 for outer and +/-2 for inner that is used to fill properly during |
| 362 | // subsequent polygon generation. |
| 363 | e->fWinding = SkScalarCopySign(e->fType == EdgeType::kInner ? 2 : 1, |
| 364 | e->fWinding); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 365 | } |
| 366 | } |
| 367 | e = prev; |
| 368 | } |
| 369 | Edge* prev = leftEnclosingEdge; |
| 370 | for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) { |
| 371 | if (prev) { |
| 372 | e->fWinding += prev->fWinding; |
| 373 | } |
| 374 | activeEdges.insert(e, prev); |
| 375 | prev = e; |
| 376 | } |
| 377 | } |
| 378 | bool complex = events.size() > 0; |
| 379 | |
| 380 | TESS_LOG("\ncollapsing overlap regions\n"); |
| 381 | TESS_LOG("skeleton before:\n"); |
| 382 | dump_skel(ssEdges); |
| 383 | while (events.size() > 0) { |
| 384 | Event* event = events.top(); |
| 385 | events.pop(); |
| 386 | event->apply(mesh, c, &events, this); |
| 387 | } |
| 388 | TESS_LOG("skeleton after:\n"); |
| 389 | dump_skel(ssEdges); |
| 390 | for (SSEdge* edge : ssEdges) { |
| 391 | if (Edge* e = edge->fEdge) { |
Chris Dalton | 0879da0 | 2021-01-29 13:57:25 -0700 | [diff] [blame] | 392 | this->makeConnectingEdge(edge->fPrev->fVertex, edge->fNext->fVertex, e->fType, c, 0); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 393 | } |
| 394 | } |
| 395 | return complex; |
| 396 | } |
| 397 | |
| 398 | static bool inversion(Vertex* prev, Vertex* next, Edge* origEdge, const Comparator& c) { |
| 399 | if (!prev || !next) { |
| 400 | return true; |
| 401 | } |
| 402 | int winding = c.sweep_lt(prev->fPoint, next->fPoint) ? 1 : -1; |
| 403 | return winding != origEdge->fWinding; |
| 404 | } |
| 405 | |
| 406 | // Stage 5d: Displace edges by half a pixel inward and outward along their normals. Intersect to |
| 407 | // find new vertices, and set zero alpha on the exterior and one alpha on the interior. Build a |
| 408 | // new antialiased mesh from those vertices. |
| 409 | |
| 410 | void GrAATriangulator::strokeBoundary(EdgeList* boundary, VertexList* innerMesh, |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 411 | const Comparator& c) const { |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 412 | TESS_LOG("\nstroking boundary\n"); |
| 413 | // A boundary with fewer than 3 edges is degenerate. |
| 414 | if (!boundary->fHead || !boundary->fHead->fRight || !boundary->fHead->fRight->fRight) { |
| 415 | return; |
| 416 | } |
| 417 | Edge* prevEdge = boundary->fTail; |
| 418 | Vertex* prevV = prevEdge->fWinding > 0 ? prevEdge->fTop : prevEdge->fBottom; |
| 419 | SkVector prevNormal; |
| 420 | get_edge_normal(prevEdge, &prevNormal); |
| 421 | double radius = 0.5; |
| 422 | Line prevInner(prevEdge->fLine); |
| 423 | prevInner.fC -= radius; |
| 424 | Line prevOuter(prevEdge->fLine); |
| 425 | prevOuter.fC += radius; |
| 426 | VertexList innerVertices; |
| 427 | VertexList outerVertices; |
| 428 | bool innerInversion = true; |
| 429 | bool outerInversion = true; |
| 430 | for (Edge* e = boundary->fHead; e != nullptr; e = e->fRight) { |
| 431 | Vertex* v = e->fWinding > 0 ? e->fTop : e->fBottom; |
| 432 | SkVector normal; |
| 433 | get_edge_normal(e, &normal); |
| 434 | Line inner(e->fLine); |
| 435 | inner.fC -= radius; |
| 436 | Line outer(e->fLine); |
| 437 | outer.fC += radius; |
| 438 | SkPoint innerPoint, outerPoint; |
| 439 | TESS_LOG("stroking vertex %g (%g, %g)\n", v->fID, v->fPoint.fX, v->fPoint.fY); |
| 440 | if (!prevEdge->fLine.nearParallel(e->fLine) && prevInner.intersect(inner, &innerPoint) && |
| 441 | prevOuter.intersect(outer, &outerPoint)) { |
| 442 | float cosAngle = normal.dot(prevNormal); |
| 443 | if (cosAngle < -kCosMiterAngle) { |
| 444 | Vertex* nextV = e->fWinding > 0 ? e->fBottom : e->fTop; |
| 445 | |
| 446 | // This is a pointy vertex whose angle is smaller than the threshold; miter it. |
| 447 | Line bisector(innerPoint, outerPoint); |
| 448 | Line tangent(v->fPoint, v->fPoint + SkPoint::Make(bisector.fA, bisector.fB)); |
| 449 | if (tangent.fA == 0 && tangent.fB == 0) { |
| 450 | continue; |
| 451 | } |
| 452 | tangent.normalize(); |
| 453 | Line innerTangent(tangent); |
| 454 | Line outerTangent(tangent); |
| 455 | innerTangent.fC -= 0.5; |
| 456 | outerTangent.fC += 0.5; |
| 457 | SkPoint innerPoint1, innerPoint2, outerPoint1, outerPoint2; |
| 458 | if (prevNormal.cross(normal) > 0) { |
| 459 | // Miter inner points |
| 460 | if (!innerTangent.intersect(prevInner, &innerPoint1) || |
| 461 | !innerTangent.intersect(inner, &innerPoint2) || |
| 462 | !outerTangent.intersect(bisector, &outerPoint)) { |
| 463 | continue; |
| 464 | } |
| 465 | Line prevTangent(prevV->fPoint, |
| 466 | prevV->fPoint + SkVector::Make(prevOuter.fA, prevOuter.fB)); |
| 467 | Line nextTangent(nextV->fPoint, |
| 468 | nextV->fPoint + SkVector::Make(outer.fA, outer.fB)); |
| 469 | if (prevTangent.dist(outerPoint) > 0) { |
| 470 | bisector.intersect(prevTangent, &outerPoint); |
| 471 | } |
| 472 | if (nextTangent.dist(outerPoint) < 0) { |
| 473 | bisector.intersect(nextTangent, &outerPoint); |
| 474 | } |
| 475 | outerPoint1 = outerPoint2 = outerPoint; |
| 476 | } else { |
| 477 | // Miter outer points |
| 478 | if (!outerTangent.intersect(prevOuter, &outerPoint1) || |
| 479 | !outerTangent.intersect(outer, &outerPoint2)) { |
| 480 | continue; |
| 481 | } |
| 482 | Line prevTangent(prevV->fPoint, |
| 483 | prevV->fPoint + SkVector::Make(prevInner.fA, prevInner.fB)); |
| 484 | Line nextTangent(nextV->fPoint, |
| 485 | nextV->fPoint + SkVector::Make(inner.fA, inner.fB)); |
| 486 | if (prevTangent.dist(innerPoint) > 0) { |
| 487 | bisector.intersect(prevTangent, &innerPoint); |
| 488 | } |
| 489 | if (nextTangent.dist(innerPoint) < 0) { |
| 490 | bisector.intersect(nextTangent, &innerPoint); |
| 491 | } |
| 492 | innerPoint1 = innerPoint2 = innerPoint; |
| 493 | } |
| 494 | if (!innerPoint1.isFinite() || !innerPoint2.isFinite() || |
| 495 | !outerPoint1.isFinite() || !outerPoint2.isFinite()) { |
| 496 | continue; |
| 497 | } |
| 498 | TESS_LOG("inner (%g, %g), (%g, %g), ", |
| 499 | innerPoint1.fX, innerPoint1.fY, innerPoint2.fX, innerPoint2.fY); |
| 500 | TESS_LOG("outer (%g, %g), (%g, %g)\n", |
| 501 | outerPoint1.fX, outerPoint1.fY, outerPoint2.fX, outerPoint2.fY); |
Chris Dalton | 8a42b09 | 2021-01-21 22:09:26 -0700 | [diff] [blame] | 502 | Vertex* innerVertex1 = fAlloc->make<Vertex>(innerPoint1, 255); |
| 503 | Vertex* innerVertex2 = fAlloc->make<Vertex>(innerPoint2, 255); |
| 504 | Vertex* outerVertex1 = fAlloc->make<Vertex>(outerPoint1, 0); |
| 505 | Vertex* outerVertex2 = fAlloc->make<Vertex>(outerPoint2, 0); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 506 | innerVertex1->fPartner = outerVertex1; |
| 507 | innerVertex2->fPartner = outerVertex2; |
| 508 | outerVertex1->fPartner = innerVertex1; |
| 509 | outerVertex2->fPartner = innerVertex2; |
| 510 | if (!inversion(innerVertices.fTail, innerVertex1, prevEdge, c)) { |
| 511 | innerInversion = false; |
| 512 | } |
| 513 | if (!inversion(outerVertices.fTail, outerVertex1, prevEdge, c)) { |
| 514 | outerInversion = false; |
| 515 | } |
| 516 | innerVertices.append(innerVertex1); |
| 517 | innerVertices.append(innerVertex2); |
| 518 | outerVertices.append(outerVertex1); |
| 519 | outerVertices.append(outerVertex2); |
| 520 | } else { |
| 521 | TESS_LOG("inner (%g, %g), ", innerPoint.fX, innerPoint.fY); |
| 522 | TESS_LOG("outer (%g, %g)\n", outerPoint.fX, outerPoint.fY); |
Chris Dalton | 8a42b09 | 2021-01-21 22:09:26 -0700 | [diff] [blame] | 523 | Vertex* innerVertex = fAlloc->make<Vertex>(innerPoint, 255); |
| 524 | Vertex* outerVertex = fAlloc->make<Vertex>(outerPoint, 0); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 525 | innerVertex->fPartner = outerVertex; |
| 526 | outerVertex->fPartner = innerVertex; |
| 527 | if (!inversion(innerVertices.fTail, innerVertex, prevEdge, c)) { |
| 528 | innerInversion = false; |
| 529 | } |
| 530 | if (!inversion(outerVertices.fTail, outerVertex, prevEdge, c)) { |
| 531 | outerInversion = false; |
| 532 | } |
| 533 | innerVertices.append(innerVertex); |
| 534 | outerVertices.append(outerVertex); |
| 535 | } |
| 536 | } |
| 537 | prevInner = inner; |
| 538 | prevOuter = outer; |
| 539 | prevV = v; |
| 540 | prevEdge = e; |
| 541 | prevNormal = normal; |
| 542 | } |
| 543 | if (!inversion(innerVertices.fTail, innerVertices.fHead, prevEdge, c)) { |
| 544 | innerInversion = false; |
| 545 | } |
| 546 | if (!inversion(outerVertices.fTail, outerVertices.fHead, prevEdge, c)) { |
| 547 | outerInversion = false; |
| 548 | } |
| 549 | // Outer edges get 1 winding, and inner edges get -2 winding. This ensures that the interior |
| 550 | // is always filled (1 + -2 = -1 for normal cases, 1 + 2 = 3 for thin features where the |
| 551 | // interior inverts). |
| 552 | // For total inversion cases, the shape has now reversed handedness, so invert the winding |
| 553 | // so it will be detected during collapseOverlapRegions(). |
| 554 | int innerWinding = innerInversion ? 2 : -2; |
| 555 | int outerWinding = outerInversion ? -1 : 1; |
| 556 | for (Vertex* v = innerVertices.fHead; v && v->fNext; v = v->fNext) { |
Chris Dalton | 0879da0 | 2021-01-29 13:57:25 -0700 | [diff] [blame] | 557 | this->makeConnectingEdge(v, v->fNext, EdgeType::kInner, c, innerWinding); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 558 | } |
Chris Dalton | 0879da0 | 2021-01-29 13:57:25 -0700 | [diff] [blame] | 559 | this->makeConnectingEdge(innerVertices.fTail, innerVertices.fHead, EdgeType::kInner, c, |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 560 | innerWinding); |
| 561 | for (Vertex* v = outerVertices.fHead; v && v->fNext; v = v->fNext) { |
Chris Dalton | 0879da0 | 2021-01-29 13:57:25 -0700 | [diff] [blame] | 562 | this->makeConnectingEdge(v, v->fNext, EdgeType::kOuter, c, outerWinding); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 563 | } |
| 564 | this->makeConnectingEdge(outerVertices.fTail, outerVertices.fHead, EdgeType::kOuter, c, |
Chris Dalton | 0879da0 | 2021-01-29 13:57:25 -0700 | [diff] [blame] | 565 | outerWinding); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 566 | innerMesh->append(innerVertices); |
| 567 | fOuterMesh.append(outerVertices); |
| 568 | } |
| 569 | |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 570 | void GrAATriangulator::extractBoundary(EdgeList* boundary, Edge* e) const { |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 571 | TESS_LOG("\nextracting boundary\n"); |
| 572 | bool down = this->applyFillType(e->fWinding); |
| 573 | Vertex* start = down ? e->fTop : e->fBottom; |
| 574 | do { |
| 575 | e->fWinding = down ? 1 : -1; |
| 576 | Edge* next; |
| 577 | e->fLine.normalize(); |
| 578 | e->fLine = e->fLine * e->fWinding; |
| 579 | boundary->append(e); |
| 580 | if (down) { |
| 581 | // Find outgoing edge, in clockwise order. |
| 582 | if ((next = e->fNextEdgeAbove)) { |
| 583 | down = false; |
| 584 | } else if ((next = e->fBottom->fLastEdgeBelow)) { |
| 585 | down = true; |
| 586 | } else if ((next = e->fPrevEdgeAbove)) { |
| 587 | down = false; |
| 588 | } |
| 589 | } else { |
| 590 | // Find outgoing edge, in counter-clockwise order. |
| 591 | if ((next = e->fPrevEdgeBelow)) { |
| 592 | down = true; |
| 593 | } else if ((next = e->fTop->fFirstEdgeAbove)) { |
| 594 | down = false; |
| 595 | } else if ((next = e->fNextEdgeBelow)) { |
| 596 | down = true; |
| 597 | } |
| 598 | } |
| 599 | e->disconnect(); |
| 600 | e = next; |
| 601 | } while (e && (down ? e->fTop : e->fBottom) != start); |
| 602 | } |
| 603 | |
| 604 | // Stage 5b: Extract boundaries from mesh, simplify and stroke them into a new mesh. |
| 605 | |
| 606 | void GrAATriangulator::extractBoundaries(const VertexList& inMesh, VertexList* innerVertices, |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 607 | const Comparator& c) const { |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 608 | this->removeNonBoundaryEdges(inMesh); |
| 609 | for (Vertex* v = inMesh.fHead; v; v = v->fNext) { |
| 610 | while (v->fFirstEdgeBelow) { |
| 611 | EdgeList boundary; |
| 612 | this->extractBoundary(&boundary, v->fFirstEdgeBelow); |
| 613 | this->simplifyBoundary(&boundary, c); |
| 614 | this->strokeBoundary(&boundary, innerVertices, c); |
| 615 | } |
| 616 | } |
| 617 | } |
| 618 | |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 619 | Poly* GrAATriangulator::tessellate(const VertexList& mesh, const Comparator& c) const { |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 620 | VertexList innerMesh; |
| 621 | this->extractBoundaries(mesh, &innerMesh, c); |
| 622 | SortMesh(&innerMesh, c); |
| 623 | SortMesh(&fOuterMesh, c); |
Chris Dalton | 0879da0 | 2021-01-29 13:57:25 -0700 | [diff] [blame] | 624 | this->mergeCoincidentVertices(&innerMesh, c); |
| 625 | bool was_complex = this->mergeCoincidentVertices(&fOuterMesh, c); |
| 626 | auto result = this->simplify(&innerMesh, c); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 627 | was_complex = (SimplifyResult::kFoundSelfIntersection == result) || was_complex; |
Chris Dalton | 0879da0 | 2021-01-29 13:57:25 -0700 | [diff] [blame] | 628 | result = this->simplify(&fOuterMesh, c); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 629 | was_complex = (SimplifyResult::kFoundSelfIntersection == result) || was_complex; |
| 630 | TESS_LOG("\ninner mesh before:\n"); |
| 631 | DUMP_MESH(innerMesh); |
| 632 | TESS_LOG("\nouter mesh before:\n"); |
| 633 | DUMP_MESH(fOuterMesh); |
| 634 | EventComparator eventLT(EventComparator::Op::kLessThan); |
| 635 | EventComparator eventGT(EventComparator::Op::kGreaterThan); |
| 636 | was_complex = this->collapseOverlapRegions(&innerMesh, c, eventLT) || was_complex; |
| 637 | was_complex = this->collapseOverlapRegions(&fOuterMesh, c, eventGT) || was_complex; |
| 638 | if (was_complex) { |
| 639 | TESS_LOG("found complex mesh; taking slow path\n"); |
| 640 | VertexList aaMesh; |
| 641 | TESS_LOG("\ninner mesh after:\n"); |
| 642 | DUMP_MESH(innerMesh); |
| 643 | TESS_LOG("\nouter mesh after:\n"); |
| 644 | DUMP_MESH(fOuterMesh); |
| 645 | this->connectPartners(&fOuterMesh, c); |
| 646 | this->connectPartners(&innerMesh, c); |
| 647 | SortedMerge(&innerMesh, &fOuterMesh, &aaMesh, c); |
Michael Ludwig | 799658f | 2021-05-06 16:41:38 -0400 | [diff] [blame] | 648 | TESS_LOG("\nmerged mesh:\n"); |
| 649 | DUMP_MESH(aaMesh); |
Chris Dalton | 0879da0 | 2021-01-29 13:57:25 -0700 | [diff] [blame] | 650 | this->mergeCoincidentVertices(&aaMesh, c); |
| 651 | result = this->simplify(&aaMesh, c); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 652 | TESS_LOG("combined and simplified mesh:\n"); |
| 653 | DUMP_MESH(aaMesh); |
| 654 | fOuterMesh.fHead = fOuterMesh.fTail = nullptr; |
| 655 | return this->GrTriangulator::tessellate(aaMesh, c); |
| 656 | } else { |
| 657 | TESS_LOG("no complex polygons; taking fast path\n"); |
| 658 | return this->GrTriangulator::tessellate(innerMesh, c); |
| 659 | } |
| 660 | } |
| 661 | |
Chris Dalton | 330cfa4 | 2021-01-27 18:24:48 -0700 | [diff] [blame] | 662 | int GrAATriangulator::polysToAATriangles(Poly* polys, |
| 663 | GrEagerVertexAllocator* vertexAllocator) const { |
Chris Dalton | d538479 | 2021-01-20 15:43:24 -0700 | [diff] [blame] | 664 | int64_t count64 = CountPoints(polys, SkPathFillType::kWinding); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 665 | // Count the points from the outer mesh. |
| 666 | for (Vertex* v = fOuterMesh.fHead; v; v = v->fNext) { |
| 667 | for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) { |
Chris Dalton | d538479 | 2021-01-20 15:43:24 -0700 | [diff] [blame] | 668 | count64 += TRIANGULATOR_WIREFRAME ? 12 : 6; |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 669 | } |
| 670 | } |
Chris Dalton | d538479 | 2021-01-20 15:43:24 -0700 | [diff] [blame] | 671 | if (0 == count64 || count64 > SK_MaxS32) { |
| 672 | return 0; |
| 673 | } |
| 674 | int count = count64; |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 675 | |
Chris Dalton | d538479 | 2021-01-20 15:43:24 -0700 | [diff] [blame] | 676 | size_t vertexStride = sizeof(SkPoint) + sizeof(float); |
| 677 | void* verts = vertexAllocator->lock(vertexStride, count); |
| 678 | if (!verts) { |
| 679 | SkDebugf("Could not allocate vertices\n"); |
| 680 | return 0; |
| 681 | } |
| 682 | |
| 683 | TESS_LOG("emitting %d verts\n", count); |
Chris Dalton | 0879da0 | 2021-01-29 13:57:25 -0700 | [diff] [blame] | 684 | void* end = this->polysToTriangles(polys, verts, SkPathFillType::kWinding); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 685 | // Emit the triangles from the outer mesh. |
| 686 | for (Vertex* v = fOuterMesh.fHead; v; v = v->fNext) { |
| 687 | for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) { |
| 688 | Vertex* v0 = e->fTop; |
| 689 | Vertex* v1 = e->fBottom; |
| 690 | Vertex* v2 = e->fBottom->fPartner; |
| 691 | Vertex* v3 = e->fTop->fPartner; |
Chris Dalton | 0879da0 | 2021-01-29 13:57:25 -0700 | [diff] [blame] | 692 | end = this->emitTriangle(v0, v1, v2, 0/*winding*/, end); |
| 693 | end = this->emitTriangle(v0, v2, v3, 0/*winding*/, end); |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 694 | } |
| 695 | } |
Chris Dalton | d538479 | 2021-01-20 15:43:24 -0700 | [diff] [blame] | 696 | |
| 697 | int actualCount = static_cast<int>((static_cast<uint8_t*>(end) - static_cast<uint8_t*>(verts)) |
| 698 | / vertexStride); |
| 699 | SkASSERT(actualCount <= count); |
| 700 | vertexAllocator->unlock(actualCount); |
| 701 | return actualCount; |
Chris Dalton | 47114db | 2021-01-06 00:35:20 -0700 | [diff] [blame] | 702 | } |