Flutter Engine
The Flutter Engine
GrTriangulator.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2015 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
9
11#include "include/core/SkRect.h"
16#include "src/base/SkVx.h"
17#include "src/core/SkGeometry.h"
23
24#include <algorithm>
25#include <cstddef>
26#include <limits>
27#include <memory>
28#include <tuple>
29#include <utility>
30
31#if !defined(SK_ENABLE_OPTIMIZE_SIZE)
32
33#if TRIANGULATOR_LOGGING
34#define TESS_LOG printf
35#define DUMP_MESH(M) (M).dump()
36#else
37#define TESS_LOG(...)
38#define DUMP_MESH(M)
39#endif
40
50
51template <class T, T* T::*Prev, T* T::*Next>
52static void list_insert(T* t, T* prev, T* next, T** head, T** tail) {
53 t->*Prev = prev;
54 t->*Next = next;
55 if (prev) {
56 prev->*Next = t;
57 } else if (head) {
58 *head = t;
59 }
60 if (next) {
61 next->*Prev = t;
62 } else if (tail) {
63 *tail = t;
64 }
65}
66
67template <class T, T* T::*Prev, T* T::*Next>
68static void list_remove(T* t, T** head, T** tail) {
69 if (t->*Prev) {
70 t->*Prev->*Next = t->*Next;
71 } else if (head) {
72 *head = t->*Next;
73 }
74 if (t->*Next) {
75 t->*Next->*Prev = t->*Prev;
76 } else if (tail) {
77 *tail = t->*Prev;
78 }
79 t->*Prev = t->*Next = nullptr;
80}
81
82typedef bool (*CompareFunc)(const SkPoint& a, const SkPoint& b);
83
84static bool sweep_lt_horiz(const SkPoint& a, const SkPoint& b) {
85 return a.fX < b.fX || (a.fX == b.fX && a.fY > b.fY);
86}
87
88static bool sweep_lt_vert(const SkPoint& a, const SkPoint& b) {
89 return a.fY < b.fY || (a.fY == b.fY && a.fX < b.fX);
90}
91
94}
95
97 bool emitCoverage,
99 data << v->fPoint;
100
101 if (emitCoverage) {
102 data << GrNormalizeByteToFloat(v->fAlpha);
103 }
104
105 return data;
106}
107
109 bool emitCoverage, skgpu::VertexWriter data) {
110 TESS_LOG("emit_triangle %g (%g, %g) %d\n", v0->fID, v0->fPoint.fX, v0->fPoint.fY, v0->fAlpha);
111 TESS_LOG(" %g (%g, %g) %d\n", v1->fID, v1->fPoint.fX, v1->fPoint.fY, v1->fAlpha);
112 TESS_LOG(" %g (%g, %g) %d\n", v2->fID, v2->fPoint.fX, v2->fPoint.fY, v2->fAlpha);
113#if TRIANGULATOR_WIREFRAME
114 data = emit_vertex(v0, emitCoverage, std::move(data));
115 data = emit_vertex(v1, emitCoverage, std::move(data));
116 data = emit_vertex(v1, emitCoverage, std::move(data));
117 data = emit_vertex(v2, emitCoverage, std::move(data));
118 data = emit_vertex(v2, emitCoverage, std::move(data));
119 data = emit_vertex(v0, emitCoverage, std::move(data));
120#else
121 data = emit_vertex(v0, emitCoverage, std::move(data));
122 data = emit_vertex(v1, emitCoverage, std::move(data));
123 data = emit_vertex(v2, emitCoverage, std::move(data));
124#endif
125 return data;
126}
127
129 list_insert<Vertex, &Vertex::fPrev, &Vertex::fNext>(v, prev, next, &fHead, &fTail);
130}
131
133 list_remove<Vertex, &Vertex::fPrev, &Vertex::fNext>(v, &fHead, &fTail);
134}
135
136// Round to nearest quarter-pixel. This is used for screenspace tessellation.
137
138static inline void round(SkPoint* p) {
139 p->fX = SkScalarRoundToScalar(p->fX * 4.0f) * 0.25f;
140 p->fY = SkScalarRoundToScalar(p->fY * 4.0f) * 0.25f;
141}
142
143static inline SkScalar double_to_clamped_scalar(double d) {
144 // Clamps large values to what's finitely representable when cast back to a float.
145 static const double kMaxLimit = (double) SK_ScalarMax;
146 // It's not perfect, but a using a value larger than float_min helps protect from denormalized
147 // values and ill-conditions in intermediate calculations on coordinates.
148 static const double kNearZeroLimit = 16 * (double) std::numeric_limits<float>::min();
149 if (std::abs(d) < kNearZeroLimit) {
150 d = 0.f;
151 }
152 return SkDoubleToScalar(std::max(-kMaxLimit, std::min(d, kMaxLimit)));
153}
154
155bool GrTriangulator::Line::intersect(const Line& other, SkPoint* point) const {
156 double denom = fA * other.fB - fB * other.fA;
157 if (denom == 0.0) {
158 return false;
159 }
160 double scale = 1.0 / denom;
161 point->fX = double_to_clamped_scalar((fB * other.fC - other.fB * fC) * scale);
162 point->fY = double_to_clamped_scalar((other.fA * fC - fA * other.fC) * scale);
163 round(point);
164 return point->isFinite();
165}
166
167// If the edge's vertices differ by many orders of magnitude, the computed line equation can have
168// significant error in its distance and intersection tests. To avoid this, we recursively subdivide
169// long edges and effectively perform a binary search to perform a more accurate intersection test.
170static bool edge_line_needs_recursion(const SkPoint& p0, const SkPoint& p1) {
171 // ilogbf(0) returns an implementation-defined constant, but we are choosing to saturate
172 // negative exponents to 0 for comparisons sake. We're only trying to recurse on lines with
173 // very large coordinates.
174 int expDiffX = std::abs((std::abs(p0.fX) < 1.f ? 0 : std::ilogbf(p0.fX)) -
175 (std::abs(p1.fX) < 1.f ? 0 : std::ilogbf(p1.fX)));
176 int expDiffY = std::abs((std::abs(p0.fY) < 1.f ? 0 : std::ilogbf(p0.fY)) -
177 (std::abs(p1.fY) < 1.f ? 0 : std::ilogbf(p1.fY)));
178 // Differ by more than 2^20, or roughly a factor of one million.
179 return expDiffX > 20 || expDiffY > 20;
180}
181
182static bool recursive_edge_intersect(const Line& u, SkPoint u0, SkPoint u1,
183 const Line& v, SkPoint v0, SkPoint v1,
184 SkPoint* p, double* s, double* t) {
185 // First check if the bounding boxes of [u0,u1] intersects [v0,v1]. If they do not, then the
186 // two line segments cannot intersect in their domain (even if the lines themselves might).
187 // - don't use SkRect::intersect since the vertices aren't sorted and horiz/vertical lines
188 // appear as empty rects, which then never "intersect" according to SkRect.
189 if (std::min(u0.fX, u1.fX) > std::max(v0.fX, v1.fX) ||
190 std::max(u0.fX, u1.fX) < std::min(v0.fX, v1.fX) ||
191 std::min(u0.fY, u1.fY) > std::max(v0.fY, v1.fY) ||
192 std::max(u0.fY, u1.fY) < std::min(v0.fY, v1.fY)) {
193 return false;
194 }
195
196 // Compute intersection based on current segment vertices; if an intersection is found but the
197 // vertices differ too much in magnitude, we recurse using the midpoint of the segment to
198 // reject false positives. We don't currently try to avoid false negatives (e.g. large magnitude
199 // line reports no intersection but there is one).
200 double denom = u.fA * v.fB - u.fB * v.fA;
201 if (denom == 0.0) {
202 return false;
203 }
204 double dx = static_cast<double>(v0.fX) - u0.fX;
205 double dy = static_cast<double>(v0.fY) - u0.fY;
206 double sNumer = dy * v.fB + dx * v.fA;
207 double tNumer = dy * u.fB + dx * u.fA;
208 // If (sNumer / denom) or (tNumer / denom) is not in [0..1], exit early.
209 // This saves us doing the divide below unless absolutely necessary.
210 if (denom > 0.0 ? (sNumer < 0.0 || sNumer > denom || tNumer < 0.0 || tNumer > denom)
211 : (sNumer > 0.0 || sNumer < denom || tNumer > 0.0 || tNumer < denom)) {
212 return false;
213 }
214
215 *s = sNumer / denom;
216 *t = tNumer / denom;
217 SkASSERT(*s >= 0.0 && *s <= 1.0 && *t >= 0.0 && *t <= 1.0);
218
219 const bool uNeedsSplit = edge_line_needs_recursion(u0, u1);
220 const bool vNeedsSplit = edge_line_needs_recursion(v0, v1);
221 if (!uNeedsSplit && !vNeedsSplit) {
222 p->fX = double_to_clamped_scalar(u0.fX - (*s) * u.fB);
223 p->fY = double_to_clamped_scalar(u0.fY + (*s) * u.fA);
224 return true;
225 } else {
226 double sScale = 1.0, sShift = 0.0;
227 double tScale = 1.0, tShift = 0.0;
228
229 if (uNeedsSplit) {
230 SkPoint uM = {(float) (0.5 * u0.fX + 0.5 * u1.fX),
231 (float) (0.5 * u0.fY + 0.5 * u1.fY)};
232 sScale = 0.5;
233 if (*s >= 0.5) {
234 u0 = uM;
235 sShift = 0.5;
236 } else {
237 u1 = uM;
238 }
239 }
240 if (vNeedsSplit) {
241 SkPoint vM = {(float) (0.5 * v0.fX + 0.5 * v1.fX),
242 (float) (0.5 * v0.fY + 0.5 * v1.fY)};
243 tScale = 0.5;
244 if (*t >= 0.5) {
245 v0 = vM;
246 tShift = 0.5;
247 } else {
248 v1 = vM;
249 }
250 }
251
252 // Just recompute both lines, even if only one was split; we're already in a slow path.
253 if (recursive_edge_intersect(Line(u0, u1), u0, u1, Line(v0, v1), v0, v1, p, s, t)) {
254 // Adjust s and t back to full range
255 *s = sScale * (*s) + sShift;
256 *t = tScale * (*t) + tShift;
257 return true;
258 } else {
259 // False positive
260 return false;
261 }
262 }
263}
264
265bool GrTriangulator::Edge::intersect(const Edge& other, SkPoint* p, uint8_t* alpha) const {
266 TESS_LOG("intersecting %g -> %g with %g -> %g\n",
267 fTop->fID, fBottom->fID, other.fTop->fID, other.fBottom->fID);
268 if (fTop == other.fTop || fBottom == other.fBottom ||
269 fTop == other.fBottom || fBottom == other.fTop) {
270 // If the two edges share a vertex by construction, they have already been split and
271 // shouldn't be considered "intersecting" anymore.
272 return false;
273 }
274
275 double s, t; // needed to interpolate vertex alpha
276 const bool intersects = recursive_edge_intersect(
277 fLine, fTop->fPoint, fBottom->fPoint,
278 other.fLine, other.fTop->fPoint, other.fBottom->fPoint,
279 p, &s, &t);
280 if (!intersects) {
281 return false;
282 }
283
284 if (alpha) {
285 if (fType == EdgeType::kInner || other.fType == EdgeType::kInner) {
286 // If the intersection is on any interior edge, it needs to stay fully opaque or later
287 // triangulation could leech transparency into the inner fill region.
288 *alpha = 255;
289 } else if (fType == EdgeType::kOuter && other.fType == EdgeType::kOuter) {
290 // Trivially, the intersection will be fully transparent since since it is by
291 // construction on the outer edge.
292 *alpha = 0;
293 } else {
294 // Could be two connectors crossing, or a connector crossing an outer edge.
295 // Take the max interpolated alpha
297 *alpha = std::max((1.0 - s) * fTop->fAlpha + s * fBottom->fAlpha,
298 (1.0 - t) * other.fTop->fAlpha + t * other.fBottom->fAlpha);
299 }
300 }
301 return true;
302}
303
305 list_insert<Edge, &Edge::fLeft, &Edge::fRight>(edge, prev, next, &fHead, &fTail);
306}
307
309 TESS_LOG("removing edge %g -> %g\n", edge->fTop->fID, edge->fBottom->fID);
310 // SkASSERT(this->contains(edge)); // Leave this here for future debugging.
311 if (!this->contains(edge)) {
312 return false;
313 }
314 list_remove<Edge, &Edge::fLeft, &Edge::fRight>(edge, &fHead, &fTail);
315 return true;
316}
317
319 if (fSide == kRight_Side) {
321 list_insert<Edge, &Edge::fRightPolyPrev, &Edge::fRightPolyNext>(
322 edge, fLastEdge, nullptr, &fFirstEdge, &fLastEdge);
323 edge->fUsedInRightPoly = true;
324 } else {
326 list_insert<Edge, &Edge::fLeftPolyPrev, &Edge::fLeftPolyNext>(
327 edge, fLastEdge, nullptr, &fFirstEdge, &fLastEdge);
328 edge->fUsedInLeftPoly = true;
329 }
330}
331
334 SkASSERT(monotonePoly->fWinding != 0);
335 Edge* e = monotonePoly->fFirstEdge;
336 VertexList vertices;
337 vertices.append(e->fTop);
338 int count = 1;
339 while (e != nullptr) {
340 if (kRight_Side == monotonePoly->fSide) {
341 vertices.append(e->fBottom);
342 e = e->fRightPolyNext;
343 } else {
344 vertices.prepend(e->fBottom);
345 e = e->fLeftPolyNext;
346 }
347 count++;
348 }
349 Vertex* first = vertices.fHead;
350 Vertex* v = first->fNext;
351 while (v != vertices.fTail) {
352 SkASSERT(v && v->fPrev && v->fNext);
353 Vertex* prev = v->fPrev;
354 Vertex* curr = v;
355 Vertex* next = v->fNext;
356 if (count == 3) {
357 return this->emitTriangle(prev, curr, next, monotonePoly->fWinding, std::move(data));
358 }
359 double ax = static_cast<double>(curr->fPoint.fX) - prev->fPoint.fX;
360 double ay = static_cast<double>(curr->fPoint.fY) - prev->fPoint.fY;
361 double bx = static_cast<double>(next->fPoint.fX) - curr->fPoint.fX;
362 double by = static_cast<double>(next->fPoint.fY) - curr->fPoint.fY;
363 if (ax * by - ay * bx >= 0.0) {
364 data = this->emitTriangle(prev, curr, next, monotonePoly->fWinding, std::move(data));
365 v->fPrev->fNext = v->fNext;
366 v->fNext->fPrev = v->fPrev;
367 count--;
368 if (v->fPrev == first) {
369 v = v->fNext;
370 } else {
371 v = v->fPrev;
372 }
373 } else {
374 v = v->fNext;
375 }
376 }
377 return data;
378}
379
381 Vertex* prev, Vertex* curr, Vertex* next, int winding, skgpu::VertexWriter data) const {
382 if (winding > 0) {
383 // Ensure our triangles always wind in the same direction as if the path had been
384 // triangulated as a simple fan (a la red book).
386 }
387 if (fCollectBreadcrumbTriangles && abs(winding) > 1 &&
389 // The first winding count will come from the actual triangle we emit. The remaining counts
390 // come from the breadcrumb triangle.
391 fBreadcrumbList.append(fAlloc, prev->fPoint, curr->fPoint, next->fPoint, abs(winding) - 1);
392 }
393 return emit_triangle(prev, curr, next, fEmitCoverage, std::move(data));
394}
395
397 : fFirstVertex(v)
398 , fWinding(winding)
399 , fHead(nullptr)
400 , fTail(nullptr)
401 , fNext(nullptr)
402 , fPartner(nullptr)
403 , fCount(0)
404{
405#if TRIANGULATOR_LOGGING
406 static int gID = 0;
407 fID = gID++;
408 TESS_LOG("*** created Poly %d\n", fID);
409#endif
410}
411
413 TESS_LOG("addEdge (%g -> %g) to poly %d, %s side\n",
414 e->fTop->fID, e->fBottom->fID, fID, side == kLeft_Side ? "left" : "right");
415 Poly* partner = fPartner;
416 Poly* poly = this;
417 if (side == kRight_Side) {
418 if (e->fUsedInRightPoly) {
419 return this;
420 }
421 } else {
422 if (e->fUsedInLeftPoly) {
423 return this;
424 }
425 }
426 if (partner) {
427 fPartner = partner->fPartner = nullptr;
428 }
429 if (!fTail) {
430 fHead = fTail = tri->allocateMonotonePoly(e, side, fWinding);
431 fCount += 2;
432 } else if (e->fBottom == fTail->fLastEdge->fBottom) {
433 return poly;
434 } else if (side == fTail->fSide) {
435 fTail->addEdge(e);
436 fCount++;
437 } else {
438 e = tri->allocateEdge(fTail->fLastEdge->fBottom, e->fBottom, 1, EdgeType::kInner);
439 fTail->addEdge(e);
440 fCount++;
441 if (partner) {
442 partner->addEdge(e, side, tri);
443 poly = partner;
444 } else {
445 MonotonePoly* m = tri->allocateMonotonePoly(e, side, fWinding);
446 m->fPrev = fTail;
447 fTail->fNext = m;
448 fTail = m;
449 }
450 }
451 return poly;
452}
454 if (poly->fCount < 3) {
455 return data;
456 }
457 TESS_LOG("emit() %d, size %d\n", poly->fID, poly->fCount);
458 for (MonotonePoly* m = poly->fHead; m != nullptr; m = m->fNext) {
459 data = this->emitMonotonePoly(m, std::move(data));
460 }
461 return data;
462}
463
464static bool coincident(const SkPoint& a, const SkPoint& b) {
465 return a == b;
466}
467
468Poly* GrTriangulator::makePoly(Poly** head, Vertex* v, int winding) const {
469 Poly* poly = fAlloc->make<Poly>(v, winding);
470 poly->fNext = *head;
471 *head = poly;
472 return poly;
473}
474
476 Vertex* v = fAlloc->make<Vertex>(p, 255);
477#if TRIANGULATOR_LOGGING
478 static float gID = 0.0f;
479 v->fID = gID++;
480#endif
481 contour->append(v);
482}
483
484static SkScalar quad_error_at(const SkPoint pts[3], SkScalar t, SkScalar u) {
485 SkQuadCoeff quad(pts);
486 SkPoint p0 = to_point(quad.eval(t - 0.5f * u));
487 SkPoint mid = to_point(quad.eval(t));
488 SkPoint p1 = to_point(quad.eval(t + 0.5f * u));
489 if (!p0.isFinite() || !mid.isFinite() || !p1.isFinite()) {
490 return 0;
491 }
493}
494
496 VertexList* contour) const {
497 SkQuadCoeff quad(pts);
498 skvx::float2 aa = quad.fA * quad.fA;
499 SkScalar denom = 2.0f * (aa[0] + aa[1]);
500 skvx::float2 ab = quad.fA * quad.fB;
501 SkScalar t = denom ? (-ab[0] - ab[1]) / denom : 0.0f;
502 int nPoints = 1;
503 SkScalar u = 1.0f;
504 // Test possible subdivision values only at the point of maximum curvature.
505 // If it passes the flatness metric there, it'll pass everywhere.
506 while (nPoints < GrPathUtils::kMaxPointsPerCurve) {
507 u = 1.0f / nPoints;
508 if (quad_error_at(pts, t, u) < toleranceSqd) {
509 break;
510 }
511 nPoints++;
512 }
513 for (int j = 1; j <= nPoints; j++) {
514 this->appendPointToContour(to_point(quad.eval(j * u)), contour);
515 }
516}
517
518void GrTriangulator::generateCubicPoints(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
519 const SkPoint& p3, SkScalar tolSqd, VertexList* contour,
520 int pointsLeft) const {
523 if (pointsLeft < 2 || (d1 < tolSqd && d2 < tolSqd) || !SkIsFinite(d1, d2)) {
524 this->appendPointToContour(p3, contour);
525 return;
526 }
527 const SkPoint q[] = {
528 { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) },
529 { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) },
530 { SkScalarAve(p2.fX, p3.fX), SkScalarAve(p2.fY, p3.fY) }
531 };
532 const SkPoint r[] = {
533 { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) },
534 { SkScalarAve(q[1].fX, q[2].fX), SkScalarAve(q[1].fY, q[2].fY) }
535 };
536 const SkPoint s = { SkScalarAve(r[0].fX, r[1].fX), SkScalarAve(r[0].fY, r[1].fY) };
537 pointsLeft >>= 1;
538 this->generateCubicPoints(p0, q[0], r[0], s, tolSqd, contour, pointsLeft);
539 this->generateCubicPoints(s, r[1], q[2], p3, tolSqd, contour, pointsLeft);
540}
541
542// Stage 1: convert the input path to a set of linear contours (linked list of Vertices).
543
544void GrTriangulator::pathToContours(float tolerance, const SkRect& clipBounds,
545 VertexList* contours, bool* isLinear) const {
546 SkScalar toleranceSqd = tolerance * tolerance;
547 SkPoint pts[4];
548 *isLinear = true;
549 VertexList* contour = contours;
550 SkPath::Iter iter(fPath, false);
551 if (fPath.isInverseFillType()) {
552 SkPoint quad[4];
553 clipBounds.toQuad(quad);
554 for (int i = 3; i >= 0; i--) {
555 this->appendPointToContour(quad[i], contours);
556 }
557 contour++;
558 }
560 SkPath::Verb verb;
561 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
562 switch (verb) {
563 case SkPath::kConic_Verb: {
564 *isLinear = false;
565 if (toleranceSqd == 0) {
566 this->appendPointToContour(pts[2], contour);
567 break;
568 }
569 SkScalar weight = iter.conicWeight();
570 const SkPoint* quadPts = converter.computeQuads(pts, weight, toleranceSqd);
571 for (int i = 0; i < converter.countQuads(); ++i) {
572 this->appendQuadraticToContour(quadPts, toleranceSqd, contour);
573 quadPts += 2;
574 }
575 break;
576 }
578 if (contour->fHead) {
579 contour++;
580 }
581 this->appendPointToContour(pts[0], contour);
582 break;
583 case SkPath::kLine_Verb: {
584 this->appendPointToContour(pts[1], contour);
585 break;
586 }
587 case SkPath::kQuad_Verb: {
588 *isLinear = false;
589 if (toleranceSqd == 0) {
590 this->appendPointToContour(pts[2], contour);
591 break;
592 }
593 this->appendQuadraticToContour(pts, toleranceSqd, contour);
594 break;
595 }
596 case SkPath::kCubic_Verb: {
597 *isLinear = false;
598 if (toleranceSqd == 0) {
599 this->appendPointToContour(pts[3], contour);
600 break;
601 }
602 int pointsLeft = GrPathUtils::cubicPointCount(pts, tolerance);
603 this->generateCubicPoints(pts[0], pts[1], pts[2], pts[3], toleranceSqd, contour,
604 pointsLeft);
605 break;
606 }
609 break;
610 }
611 }
612}
613
614static inline bool apply_fill_type(SkPathFillType fillType, int winding) {
615 switch (fillType) {
617 return winding != 0;
619 return (winding & 1) != 0;
621 return winding == 1;
623 return (winding & 1) == 1;
624 default:
625 SkASSERT(false);
626 return false;
627 }
628}
629
630bool GrTriangulator::applyFillType(int winding) const {
631 return apply_fill_type(fPath.getFillType(), winding);
632}
633
634static inline bool apply_fill_type(SkPathFillType fillType, Poly* poly) {
635 return poly && apply_fill_type(fillType, poly->fWinding);
636}
637
640 return fAlloc->make<MonotonePoly>(edge, side, winding);
641}
642
644 ++fNumEdges;
645 return fAlloc->make<Edge>(top, bottom, winding, type);
646}
647
649 const Comparator& c) {
650 SkASSERT(prev->fPoint != next->fPoint);
651 int winding = c.sweep_lt(prev->fPoint, next->fPoint) ? 1 : -1;
652 Vertex* top = winding < 0 ? next : prev;
653 Vertex* bottom = winding < 0 ? prev : next;
654 return this->allocateEdge(top, bottom, winding, type);
655}
656
658 TESS_LOG("inserting edge %g -> %g\n", edge->fTop->fID, edge->fBottom->fID);
659 // SkASSERT(!this->contains(edge)); // Leave this here for debugging.
660 if (this->contains(edge)) {
661 return false;
662 }
663 Edge* next = prev ? prev->fRight : fHead;
664 this->insert(edge, prev, next);
665 return true;
666}
667
669 const EdgeList& edges,
670 Edge** left, Edge**right) {
671 if (v.fFirstEdgeAbove && v.fLastEdgeAbove) {
674 return;
675 }
676 Edge* next = nullptr;
677 Edge* prev;
678 for (prev = edges.fTail; prev != nullptr; prev = prev->fLeft) {
679 if (prev->isLeftOf(v)) {
680 break;
681 }
682 next = prev;
683 }
684 *left = prev;
685 *right = next;
686}
687
689 if (fTop->fPoint == fBottom->fPoint ||
690 c.sweep_lt(fBottom->fPoint, fTop->fPoint)) {
691 return;
692 }
693 TESS_LOG("insert edge (%g -> %g) above vertex %g\n", fTop->fID, fBottom->fID, v->fID);
694 Edge* prev = nullptr;
695 Edge* next;
696 for (next = v->fFirstEdgeAbove; next; next = next->fNextEdgeAbove) {
697 if (next->isRightOf(*fTop)) {
698 break;
699 }
700 prev = next;
701 }
702 list_insert<Edge, &Edge::fPrevEdgeAbove, &Edge::fNextEdgeAbove>(
703 this, prev, next, &v->fFirstEdgeAbove, &v->fLastEdgeAbove);
704}
705
707 if (fTop->fPoint == fBottom->fPoint ||
708 c.sweep_lt(fBottom->fPoint, fTop->fPoint)) {
709 return;
710 }
711 TESS_LOG("insert edge (%g -> %g) below vertex %g\n", fTop->fID, fBottom->fID, v->fID);
712 Edge* prev = nullptr;
713 Edge* next;
714 for (next = v->fFirstEdgeBelow; next; next = next->fNextEdgeBelow) {
715 if (next->isRightOf(*fBottom)) {
716 break;
717 }
718 prev = next;
719 }
720 list_insert<Edge, &Edge::fPrevEdgeBelow, &Edge::fNextEdgeBelow>(
721 this, prev, next, &v->fFirstEdgeBelow, &v->fLastEdgeBelow);
722}
723
724static void remove_edge_above(Edge* edge) {
725 SkASSERT(edge->fTop && edge->fBottom);
726 TESS_LOG("removing edge (%g -> %g) above vertex %g\n", edge->fTop->fID, edge->fBottom->fID,
727 edge->fBottom->fID);
728 list_remove<Edge, &Edge::fPrevEdgeAbove, &Edge::fNextEdgeAbove>(
729 edge, &edge->fBottom->fFirstEdgeAbove, &edge->fBottom->fLastEdgeAbove);
730}
731
732static void remove_edge_below(Edge* edge) {
733 SkASSERT(edge->fTop && edge->fBottom);
734 TESS_LOG("removing edge (%g -> %g) below vertex %g\n",
735 edge->fTop->fID, edge->fBottom->fID, edge->fTop->fID);
736 list_remove<Edge, &Edge::fPrevEdgeBelow, &Edge::fNextEdgeBelow>(
737 edge, &edge->fTop->fFirstEdgeBelow, &edge->fTop->fLastEdgeBelow);
738}
739
741 remove_edge_above(this);
742 remove_edge_below(this);
743}
744
745static bool rewind(EdgeList* activeEdges, Vertex** current, Vertex* dst, const Comparator& c) {
746 if (!current || *current == dst || c.sweep_lt((*current)->fPoint, dst->fPoint)) {
747 return true;
748 }
749 Vertex* v = *current;
750 TESS_LOG("rewinding active edges from vertex %g to vertex %g\n", v->fID, dst->fID);
751 while (v != dst) {
752 v = v->fPrev;
753 for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) {
754 if (!activeEdges->remove(e)) {
755 return false;
756 }
757 }
758 Edge* leftEdge = v->fLeftEnclosingEdge;
759 for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) {
760 if (!activeEdges->insert(e, leftEdge)) {
761 return false;
762 }
763 leftEdge = e;
764 Vertex* top = e->fTop;
765 if (c.sweep_lt(top->fPoint, dst->fPoint) &&
766 ((top->fLeftEnclosingEdge && !top->fLeftEnclosingEdge->isLeftOf(*e->fTop)) ||
767 (top->fRightEnclosingEdge && !top->fRightEnclosingEdge->isRightOf(*e->fTop)))) {
768 dst = top;
769 }
770 }
771 }
772 *current = v;
773 return true;
774}
775
776static bool rewind_if_necessary(Edge* edge, EdgeList* activeEdges, Vertex** current,
777 const Comparator& c) {
778 if (!activeEdges || !current) {
779 return true;
780 }
781 if (!edge) {
782 return false;
783 }
784 Vertex* top = edge->fTop;
785 Vertex* bottom = edge->fBottom;
786 if (edge->fLeft) {
787 Vertex* leftTop = edge->fLeft->fTop;
788 Vertex* leftBottom = edge->fLeft->fBottom;
789 if (leftTop && leftBottom) {
790 if (c.sweep_lt(leftTop->fPoint, top->fPoint) && !edge->fLeft->isLeftOf(*top)) {
791 if (!rewind(activeEdges, current, leftTop, c)) {
792 return false;
793 }
794 } else if (c.sweep_lt(top->fPoint, leftTop->fPoint) && !edge->isRightOf(*leftTop)) {
795 if (!rewind(activeEdges, current, top, c)) {
796 return false;
797 }
798 } else if (c.sweep_lt(bottom->fPoint, leftBottom->fPoint) &&
799 !edge->fLeft->isLeftOf(*bottom)) {
800 if (!rewind(activeEdges, current, leftTop, c)) {
801 return false;
802 }
803 } else if (c.sweep_lt(leftBottom->fPoint, bottom->fPoint) &&
804 !edge->isRightOf(*leftBottom)) {
805 if (!rewind(activeEdges, current, top, c)) {
806 return false;
807 }
808 }
809 }
810 }
811 if (edge->fRight) {
812 Vertex* rightTop = edge->fRight->fTop;
813 Vertex* rightBottom = edge->fRight->fBottom;
814 if (rightTop && rightBottom) {
815 if (c.sweep_lt(rightTop->fPoint, top->fPoint) && !edge->fRight->isRightOf(*top)) {
816 if (!rewind(activeEdges, current, rightTop, c)) {
817 return false;
818 }
819 } else if (c.sweep_lt(top->fPoint, rightTop->fPoint) && !edge->isLeftOf(*rightTop)) {
820 if (!rewind(activeEdges, current, top, c)) {
821 return false;
822 }
823 } else if (c.sweep_lt(bottom->fPoint, rightBottom->fPoint) &&
824 !edge->fRight->isRightOf(*bottom)) {
825 if (!rewind(activeEdges, current, rightTop, c)) {
826 return false;
827 }
828 } else if (c.sweep_lt(rightBottom->fPoint, bottom->fPoint) &&
829 !edge->isLeftOf(*rightBottom)) {
830 if (!rewind(activeEdges, current, top, c)) {
831 return false;
832 }
833 }
834 }
835 }
836 return true;
837}
838
839bool GrTriangulator::setTop(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current,
840 const Comparator& c) const {
841 remove_edge_below(edge);
844 edge->fWinding);
845 }
846 edge->fTop = v;
847 edge->recompute();
848 edge->insertBelow(v, c);
849 if (!rewind_if_necessary(edge, activeEdges, current, c)) {
850 return false;
851 }
852 return this->mergeCollinearEdges(edge, activeEdges, current, c);
853}
854
855bool GrTriangulator::setBottom(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current,
856 const Comparator& c) const {
857 remove_edge_above(edge);
860 edge->fWinding);
861 }
862 edge->fBottom = v;
863 edge->recompute();
864 edge->insertAbove(v, c);
865 if (!rewind_if_necessary(edge, activeEdges, current, c)) {
866 return false;
867 }
868 return this->mergeCollinearEdges(edge, activeEdges, current, c);
869}
870
871bool GrTriangulator::mergeEdgesAbove(Edge* edge, Edge* other, EdgeList* activeEdges,
872 Vertex** current, const Comparator& c) const {
873 if (!edge || !other) {
874 return false;
875 }
876 if (coincident(edge->fTop->fPoint, other->fTop->fPoint)) {
877 TESS_LOG("merging coincident above edges (%g, %g) -> (%g, %g)\n",
878 edge->fTop->fPoint.fX, edge->fTop->fPoint.fY,
879 edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY);
880 if (!rewind(activeEdges, current, edge->fTop, c)) {
881 return false;
882 }
883 other->fWinding += edge->fWinding;
884 edge->disconnect();
885 edge->fTop = edge->fBottom = nullptr;
886 } else if (c.sweep_lt(edge->fTop->fPoint, other->fTop->fPoint)) {
887 if (!rewind(activeEdges, current, edge->fTop, c)) {
888 return false;
889 }
890 other->fWinding += edge->fWinding;
891 if (!this->setBottom(edge, other->fTop, activeEdges, current, c)) {
892 return false;
893 }
894 } else {
895 if (!rewind(activeEdges, current, other->fTop, c)) {
896 return false;
897 }
898 edge->fWinding += other->fWinding;
899 if (!this->setBottom(other, edge->fTop, activeEdges, current, c)) {
900 return false;
901 }
902 }
903 return true;
904}
905
906bool GrTriangulator::mergeEdgesBelow(Edge* edge, Edge* other, EdgeList* activeEdges,
907 Vertex** current, const Comparator& c) const {
908 if (!edge || !other) {
909 return false;
910 }
911 if (coincident(edge->fBottom->fPoint, other->fBottom->fPoint)) {
912 TESS_LOG("merging coincident below edges (%g, %g) -> (%g, %g)\n",
913 edge->fTop->fPoint.fX, edge->fTop->fPoint.fY,
914 edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY);
915 if (!rewind(activeEdges, current, edge->fTop, c)) {
916 return false;
917 }
918 other->fWinding += edge->fWinding;
919 edge->disconnect();
920 edge->fTop = edge->fBottom = nullptr;
921 } else if (c.sweep_lt(edge->fBottom->fPoint, other->fBottom->fPoint)) {
922 if (!rewind(activeEdges, current, other->fTop, c)) {
923 return false;
924 }
925 edge->fWinding += other->fWinding;
926 if (!this->setTop(other, edge->fBottom, activeEdges, current, c)) {
927 return false;
928 }
929 } else {
930 if (!rewind(activeEdges, current, edge->fTop, c)) {
931 return false;
932 }
933 other->fWinding += edge->fWinding;
934 if (!this->setTop(edge, other->fBottom, activeEdges, current, c)) {
935 return false;
936 }
937 }
938 return true;
939}
940
942 if (!left || !right) {
943 return false;
944 }
945 return left->fTop->fPoint == right->fTop->fPoint ||
946 !left->isLeftOf(*right->fTop) || !right->isRightOf(*left->fTop);
947}
948
950 if (!left || !right) {
951 return false;
952 }
953 return left->fBottom->fPoint == right->fBottom->fPoint ||
954 !left->isLeftOf(*right->fBottom) || !right->isRightOf(*left->fBottom);
955}
956
957bool GrTriangulator::mergeCollinearEdges(Edge* edge, EdgeList* activeEdges, Vertex** current,
958 const Comparator& c) const {
959 for (;;) {
960 if (top_collinear(edge->fPrevEdgeAbove, edge)) {
961 if (!this->mergeEdgesAbove(edge->fPrevEdgeAbove, edge, activeEdges, current, c)) {
962 return false;
963 }
964 } else if (top_collinear(edge, edge->fNextEdgeAbove)) {
965 if (!this->mergeEdgesAbove(edge->fNextEdgeAbove, edge, activeEdges, current, c)) {
966 return false;
967 }
968 } else if (bottom_collinear(edge->fPrevEdgeBelow, edge)) {
969 if (!this->mergeEdgesBelow(edge->fPrevEdgeBelow, edge, activeEdges, current, c)) {
970 return false;
971 }
972 } else if (bottom_collinear(edge, edge->fNextEdgeBelow)) {
973 if (!this->mergeEdgesBelow(edge->fNextEdgeBelow, edge, activeEdges, current, c)) {
974 return false;
975 }
976 } else {
977 break;
978 }
979 }
984 return true;
985}
986
988 Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current, const Comparator& c) {
989 if (!edge->fTop || !edge->fBottom || v == edge->fTop || v == edge->fBottom) {
990 return BoolFail::kFalse;
991 }
992 TESS_LOG("splitting edge (%g -> %g) at vertex %g (%g, %g)\n",
993 edge->fTop->fID, edge->fBottom->fID, v->fID, v->fPoint.fX, v->fPoint.fY);
994 Vertex* top;
995 Vertex* bottom;
996 int winding = edge->fWinding;
997 // Theoretically, and ideally, the edge betwee p0 and p1 is being split by v, and v is "between"
998 // the segment end points according to c. This is equivalent to p0 < v < p1. Unfortunately, if
999 // v was clamped/rounded this relation doesn't always hold.
1000 if (c.sweep_lt(v->fPoint, edge->fTop->fPoint)) {
1001 // Actually "v < p0 < p1": update 'edge' to be v->p1 and add v->p0. We flip the winding on
1002 // the new edge so that it winds as if it were p0->v.
1003 top = v;
1004 bottom = edge->fTop;
1005 winding *= -1;
1006 if (!this->setTop(edge, v, activeEdges, current, c)) {
1007 return BoolFail::kFail;
1008 }
1009 } else if (c.sweep_lt(edge->fBottom->fPoint, v->fPoint)) {
1010 // Actually "p0 < p1 < v": update 'edge' to be p0->v and add p1->v. We flip the winding on
1011 // the new edge so that it winds as if it were v->p1.
1012 top = edge->fBottom;
1013 bottom = v;
1014 winding *= -1;
1015 if (!this->setBottom(edge, v, activeEdges, current, c)) {
1016 return BoolFail::kFail;
1017 }
1018 } else {
1019 // The ideal case, "p0 < v < p1": update 'edge' to be p0->v and add v->p1. Original winding
1020 // is valid for both edges.
1021 top = v;
1022 bottom = edge->fBottom;
1023 if (!this->setBottom(edge, v, activeEdges, current, c)) {
1024 return BoolFail::kFail;
1025 }
1026 }
1027 Edge* newEdge = this->allocateEdge(top, bottom, winding, edge->fType);
1028 newEdge->insertBelow(top, c);
1029 newEdge->insertAbove(bottom, c);
1030 if (!this->mergeCollinearEdges(newEdge, activeEdges, current, c)) {
1031 return BoolFail::kFail;
1032 }
1033 return BoolFail::kTrue;
1034}
1035
1037 Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current, const Comparator& c) {
1038 if (!left->fTop || !left->fBottom || !right->fTop || !right->fBottom) {
1039 return BoolFail::kFalse;
1040 }
1041 if (left->fTop == right->fTop || left->fBottom == right->fBottom) {
1042 return BoolFail::kFalse;
1043 }
1044
1045 // Check if the lines intersect as determined by isLeftOf and isRightOf, since that is the
1046 // source of ground truth. It may suggest an intersection even if Edge::intersect() did not have
1047 // the precision to check it. In this case we are explicitly correcting the edge topology to
1048 // match the sided-ness checks.
1049 Edge* split = nullptr;
1050 Vertex* splitAt = nullptr;
1051 if (c.sweep_lt(left->fTop->fPoint, right->fTop->fPoint)) {
1052 if (!left->isLeftOf(*right->fTop)) {
1053 split = left;
1054 splitAt = right->fTop;
1055 }
1056 } else {
1057 if (!right->isRightOf(*left->fTop)) {
1058 split = right;
1059 splitAt = left->fTop;
1060 }
1061 }
1062 if (c.sweep_lt(right->fBottom->fPoint, left->fBottom->fPoint)) {
1063 if (!left->isLeftOf(*right->fBottom)) {
1064 split = left;
1065 splitAt = right->fBottom;
1066 }
1067 } else {
1068 if (!right->isRightOf(*left->fBottom)) {
1069 split = right;
1070 splitAt = left->fBottom;
1071 }
1072 }
1073
1074 if (!split) {
1075 return BoolFail::kFalse;
1076 }
1077
1078 // Rewind to the top of the edge that is "moving" since this topology correction can change the
1079 // geometry of the split edge.
1080 if (!rewind(activeEdges, current, split->fTop, c)) {
1081 return BoolFail::kFail;
1082 }
1083 return this->splitEdge(split, splitAt, activeEdges, current, c);
1084}
1085
1087 const Comparator& c, int windingScale) {
1088 if (!prev || !next || prev->fPoint == next->fPoint) {
1089 return nullptr;
1090 }
1091 Edge* edge = this->makeEdge(prev, next, type, c);
1092 edge->insertBelow(edge->fTop, c);
1093 edge->insertAbove(edge->fBottom, c);
1094 edge->fWinding *= windingScale;
1095 this->mergeCollinearEdges(edge, nullptr, nullptr, c);
1096 return edge;
1097}
1098
1100 const Comparator& c) const {
1101 TESS_LOG("found coincident verts at %g, %g; merging %g into %g\n",
1102 src->fPoint.fX, src->fPoint.fY, src->fID, dst->fID);
1103 dst->fAlpha = std::max(src->fAlpha, dst->fAlpha);
1104 if (src->fPartner) {
1105 src->fPartner->fPartner = dst;
1106 }
1107 while (Edge* edge = src->fFirstEdgeAbove) {
1108 std::ignore = this->setBottom(edge, dst, nullptr, nullptr, c);
1109 }
1110 while (Edge* edge = src->fFirstEdgeBelow) {
1111 std::ignore = this->setTop(edge, dst, nullptr, nullptr, c);
1112 }
1113 mesh->remove(src);
1114 dst->fSynthetic = true;
1115}
1116
1118 Vertex* reference, const Comparator& c) const {
1119 Vertex* prevV = reference;
1120 while (prevV && c.sweep_lt(p, prevV->fPoint)) {
1121 prevV = prevV->fPrev;
1122 }
1123 Vertex* nextV = prevV ? prevV->fNext : mesh->fHead;
1124 while (nextV && c.sweep_lt(nextV->fPoint, p)) {
1125 prevV = nextV;
1126 nextV = nextV->fNext;
1127 }
1128 Vertex* v;
1129 if (prevV && coincident(prevV->fPoint, p)) {
1130 v = prevV;
1131 } else if (nextV && coincident(nextV->fPoint, p)) {
1132 v = nextV;
1133 } else {
1134 v = fAlloc->make<Vertex>(p, alpha);
1135#if TRIANGULATOR_LOGGING
1136 if (!prevV) {
1137 v->fID = mesh->fHead->fID - 1.0f;
1138 } else if (!nextV) {
1139 v->fID = mesh->fTail->fID + 1.0f;
1140 } else {
1141 v->fID = (prevV->fID + nextV->fID) * 0.5f;
1142 }
1143#endif
1144 mesh->insert(v, prevV, nextV);
1145 }
1146 return v;
1147}
1148
1149// Clamps x and y coordinates independently, so the returned point will lie within the bounding
1150// box formed by the corners of 'min' and 'max' (although min/max here refer to the ordering
1151// imposed by 'c').
1154 // With horizontal sorting, we know min.x <= max.x, but there's no relation between
1155 // Y components unless min.x == max.x.
1156 return {SkTPin(p.fX, min.fX, max.fX),
1157 min.fY < max.fY ? SkTPin(p.fY, min.fY, max.fY)
1158 : SkTPin(p.fY, max.fY, min.fY)};
1159 } else {
1160 // And with vertical sorting, we know Y's relation but not necessarily X's.
1161 return {min.fX < max.fX ? SkTPin(p.fX, min.fX, max.fX)
1162 : SkTPin(p.fX, max.fX, min.fX),
1163 SkTPin(p.fY, min.fY, max.fY)};
1164 }
1165}
1166
1167void GrTriangulator::computeBisector(Edge* edge1, Edge* edge2, Vertex* v) const {
1168 SkASSERT(fEmitCoverage); // Edge-AA only!
1169 Line line1 = edge1->fLine;
1170 Line line2 = edge2->fLine;
1171 line1.normalize();
1172 line2.normalize();
1173 double cosAngle = line1.fA * line2.fA + line1.fB * line2.fB;
1174 if (cosAngle > 0.999) {
1175 return;
1176 }
1177 line1.fC += edge1->fWinding > 0 ? -1 : 1;
1178 line2.fC += edge2->fWinding > 0 ? -1 : 1;
1179 SkPoint p;
1180 if (line1.intersect(line2, &p)) {
1181 uint8_t alpha = edge1->fType == EdgeType::kOuter ? 255 : 0;
1182 v->fPartner = fAlloc->make<Vertex>(p, alpha);
1183 TESS_LOG("computed bisector (%g,%g) alpha %d for vertex %g\n", p.fX, p.fY, alpha, v->fID);
1184 }
1185}
1186
1188 Edge* left, Edge* right, EdgeList* activeEdges,
1189 Vertex** current, VertexList* mesh,
1190 const Comparator& c) {
1191 if (!left || !right) {
1192 return BoolFail::kFalse;
1193 }
1194 SkPoint p;
1195 uint8_t alpha;
1196 // If we are going to call intersect, then there must be tops and bottoms.
1197 if (!left->fTop || !left->fBottom || !right->fTop || !right->fBottom) {
1198 return BoolFail::kFail;
1199 }
1200 if (left->intersect(*right, &p, &alpha) && p.isFinite()) {
1201 Vertex* v;
1202 TESS_LOG("found intersection, pt is %g, %g\n", p.fX, p.fY);
1203 Vertex* top = *current;
1204 // If the intersection point is above the current vertex, rewind to the vertex above the
1205 // intersection.
1206 while (top && c.sweep_lt(p, top->fPoint)) {
1207 top = top->fPrev;
1208 }
1209
1210 // Always clamp the intersection to lie between the vertices of each segment, since
1211 // in theory that's where the intersection is, but in reality, floating point error may
1212 // have computed an intersection beyond a vertex's component(s).
1213 p = clamp(p, left->fTop->fPoint, left->fBottom->fPoint, c);
1214 p = clamp(p, right->fTop->fPoint, right->fBottom->fPoint, c);
1215
1216 if (coincident(p, left->fTop->fPoint)) {
1217 v = left->fTop;
1218 } else if (coincident(p, left->fBottom->fPoint)) {
1219 v = left->fBottom;
1220 } else if (coincident(p, right->fTop->fPoint)) {
1221 v = right->fTop;
1222 } else if (coincident(p, right->fBottom->fPoint)) {
1223 v = right->fBottom;
1224 } else {
1225 v = this->makeSortedVertex(p, alpha, mesh, top, c);
1226 if (left->fTop->fPartner) {
1227 SkASSERT(fEmitCoverage); // Edge-AA only!
1228 v->fSynthetic = true;
1229 this->computeBisector(left, right, v);
1230 }
1231 }
1232 if (!rewind(activeEdges, current, top ? top : v, c)) {
1233 return BoolFail::kFail;
1234 }
1235 if (this->splitEdge(left, v, activeEdges, current, c) == BoolFail::kFail) {
1236 return BoolFail::kFail;
1237 }
1238 if (this->splitEdge(right, v, activeEdges, current, c) == BoolFail::kFail) {
1239 return BoolFail::kFail;
1240 }
1241 v->fAlpha = std::max(v->fAlpha, alpha);
1242 return BoolFail::kTrue;
1243 }
1244 return this->intersectEdgePair(left, right, activeEdges, current, c);
1245}
1246
1247void GrTriangulator::sanitizeContours(VertexList* contours, int contourCnt) const {
1248 for (VertexList* contour = contours; contourCnt > 0; --contourCnt, ++contour) {
1249 SkASSERT(contour->fHead);
1250 Vertex* prev = contour->fTail;
1251 prev->fPoint.fX = double_to_clamped_scalar((double) prev->fPoint.fX);
1252 prev->fPoint.fY = double_to_clamped_scalar((double) prev->fPoint.fY);
1254 round(&prev->fPoint);
1255 }
1256 for (Vertex* v = contour->fHead; v;) {
1257 v->fPoint.fX = double_to_clamped_scalar((double) v->fPoint.fX);
1258 v->fPoint.fY = double_to_clamped_scalar((double) v->fPoint.fY);
1260 round(&v->fPoint);
1261 }
1262 Vertex* next = v->fNext;
1263 Vertex* nextWrap = next ? next : contour->fHead;
1264 if (coincident(prev->fPoint, v->fPoint)) {
1265 TESS_LOG("vertex %g,%g coincident; removing\n", v->fPoint.fX, v->fPoint.fY);
1266 contour->remove(v);
1267 } else if (!v->fPoint.isFinite()) {
1268 TESS_LOG("vertex %g,%g non-finite; removing\n", v->fPoint.fX, v->fPoint.fY);
1269 contour->remove(v);
1270 } else if (!fPreserveCollinearVertices &&
1271 Line(prev->fPoint, nextWrap->fPoint).dist(v->fPoint) == 0.0) {
1272 TESS_LOG("vertex %g,%g collinear; removing\n", v->fPoint.fX, v->fPoint.fY);
1273 contour->remove(v);
1274 } else {
1275 prev = v;
1276 }
1277 v = next;
1278 }
1279 }
1280}
1281
1283 if (!mesh->fHead) {
1284 return false;
1285 }
1286 bool merged = false;
1287 for (Vertex* v = mesh->fHead->fNext; v;) {
1288 Vertex* next = v->fNext;
1289 if (c.sweep_lt(v->fPoint, v->fPrev->fPoint)) {
1290 v->fPoint = v->fPrev->fPoint;
1291 }
1292 if (coincident(v->fPrev->fPoint, v->fPoint)) {
1293 this->mergeVertices(v, v->fPrev, mesh, c);
1294 merged = true;
1295 }
1296 v = next;
1297 }
1298 return merged;
1299}
1300
1301// Stage 2: convert the contours to a mesh of edges connecting the vertices.
1302
1303void GrTriangulator::buildEdges(VertexList* contours, int contourCnt, VertexList* mesh,
1304 const Comparator& c) {
1305 for (VertexList* contour = contours; contourCnt > 0; --contourCnt, ++contour) {
1306 Vertex* prev = contour->fTail;
1307 for (Vertex* v = contour->fHead; v;) {
1308 Vertex* next = v->fNext;
1309 this->makeConnectingEdge(prev, v, EdgeType::kInner, c);
1310 mesh->append(v);
1311 prev = v;
1312 v = next;
1313 }
1314 }
1315}
1316
1317template <CompareFunc sweep_lt>
1319 Vertex* a = front->fHead;
1320 Vertex* b = back->fHead;
1321 while (a && b) {
1322 if (sweep_lt(a->fPoint, b->fPoint)) {
1323 front->remove(a);
1324 result->append(a);
1325 a = front->fHead;
1326 } else {
1327 back->remove(b);
1328 result->append(b);
1329 b = back->fHead;
1330 }
1331 }
1332 result->append(*front);
1333 result->append(*back);
1334}
1335
1337 const Comparator& c) {
1339 sorted_merge<sweep_lt_horiz>(front, back, result);
1340 } else {
1341 sorted_merge<sweep_lt_vert>(front, back, result);
1342 }
1343#if TRIANGULATOR_LOGGING
1344 float id = 0.0f;
1345 for (Vertex* v = result->fHead; v; v = v->fNext) {
1346 v->fID = id++;
1347 }
1348#endif
1349}
1350
1351// Stage 3: sort the vertices by increasing sweep direction.
1352
1353template <CompareFunc sweep_lt>
1354static void merge_sort(VertexList* vertices) {
1355 Vertex* slow = vertices->fHead;
1356 if (!slow) {
1357 return;
1358 }
1359 Vertex* fast = slow->fNext;
1360 if (!fast) {
1361 return;
1362 }
1363 do {
1364 fast = fast->fNext;
1365 if (fast) {
1366 fast = fast->fNext;
1367 slow = slow->fNext;
1368 }
1369 } while (fast);
1370 VertexList front(vertices->fHead, slow);
1371 VertexList back(slow->fNext, vertices->fTail);
1372 front.fTail->fNext = back.fHead->fPrev = nullptr;
1373
1374 merge_sort<sweep_lt>(&front);
1375 merge_sort<sweep_lt>(&back);
1376
1377 vertices->fHead = vertices->fTail = nullptr;
1378 sorted_merge<sweep_lt>(&front, &back, vertices);
1379}
1380
1381#if TRIANGULATOR_LOGGING
1382void VertexList::dump() const {
1383 for (Vertex* v = fHead; v; v = v->fNext) {
1384 TESS_LOG("vertex %g (%g, %g) alpha %d", v->fID, v->fPoint.fX, v->fPoint.fY, v->fAlpha);
1385 if (Vertex* p = v->fPartner) {
1386 TESS_LOG(", partner %g (%g, %g) alpha %d\n",
1387 p->fID, p->fPoint.fX, p->fPoint.fY, p->fAlpha);
1388 } else {
1389 TESS_LOG(", null partner\n");
1390 }
1391 for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) {
1392 TESS_LOG(" edge %g -> %g, winding %d\n", e->fTop->fID, e->fBottom->fID, e->fWinding);
1393 }
1394 for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) {
1395 TESS_LOG(" edge %g -> %g, winding %d\n", e->fTop->fID, e->fBottom->fID, e->fWinding);
1396 }
1397 }
1398}
1399#endif
1400
1401#ifdef SK_DEBUG
1402static void validate_edge_pair(Edge* left, Edge* right, const Comparator& c) {
1403 if (!left || !right) {
1404 return;
1405 }
1406 if (left->fTop == right->fTop) {
1407 SkASSERT(left->isLeftOf(*right->fBottom));
1408 SkASSERT(right->isRightOf(*left->fBottom));
1409 } else if (c.sweep_lt(left->fTop->fPoint, right->fTop->fPoint)) {
1410 SkASSERT(left->isLeftOf(*right->fTop));
1411 } else {
1412 SkASSERT(right->isRightOf(*left->fTop));
1413 }
1414 if (left->fBottom == right->fBottom) {
1415 SkASSERT(left->isLeftOf(*right->fTop));
1416 SkASSERT(right->isRightOf(*left->fTop));
1417 } else if (c.sweep_lt(right->fBottom->fPoint, left->fBottom->fPoint)) {
1418 SkASSERT(left->isLeftOf(*right->fBottom));
1419 } else {
1420 SkASSERT(right->isRightOf(*left->fBottom));
1421 }
1422}
1423
1424static void validate_edge_list(EdgeList* edges, const Comparator& c) {
1425 Edge* left = edges->fHead;
1426 if (!left) {
1427 return;
1428 }
1429 for (Edge* right = left->fRight; right; right = right->fRight) {
1430 validate_edge_pair(left, right, c);
1431 left = right;
1432 }
1433}
1434#endif
1435
1436// Stage 4: Simplify the mesh by inserting new vertices at intersecting edges.
1437
1439 const Comparator& c) {
1440 TESS_LOG("simplifying complex polygons\n");
1441
1442 int initialNumEdges = fNumEdges;
1443 int numSelfIntersections = 0;
1444
1445 EdgeList activeEdges;
1447 for (Vertex* v = mesh->fHead; v != nullptr; v = v->fNext) {
1448 if (!v->isConnected()) {
1449 continue;
1450 }
1451
1452 // The max increase across all skps, svgs and gms with only the triangulating and SW path
1453 // renderers enabled and with the triangulator's maxVerbCount set to the Chrome value is
1454 // 17x.
1455 if (fNumEdges > 170*initialNumEdges) {
1457 }
1458
1459 // In pathological cases, a path can intersect itself millions of times. After 500,000
1460 // self-intersections are found, reject the path.
1461 if (numSelfIntersections > 500000) {
1463 }
1464
1465 Edge* leftEnclosingEdge;
1466 Edge* rightEnclosingEdge;
1467 bool restartChecks;
1468 do {
1469 TESS_LOG("\nvertex %g: (%g,%g), alpha %d\n",
1470 v->fID, v->fPoint.fX, v->fPoint.fY, v->fAlpha);
1471 restartChecks = false;
1472 FindEnclosingEdges(*v, activeEdges, &leftEnclosingEdge, &rightEnclosingEdge);
1473 v->fLeftEnclosingEdge = leftEnclosingEdge;
1474 v->fRightEnclosingEdge = rightEnclosingEdge;
1475 if (v->fFirstEdgeBelow) {
1476 for (Edge* edge = v->fFirstEdgeBelow; edge; edge = edge->fNextEdgeBelow) {
1478 leftEnclosingEdge, edge, &activeEdges, &v, mesh, c);
1479 if (l == BoolFail::kFail) {
1481 }
1482 if (l == BoolFail::kFalse) {
1484 edge, rightEnclosingEdge, &activeEdges, &v, mesh, c);
1485 if (r == BoolFail::kFail) {
1487 }
1488 if (r == BoolFail::kFalse) {
1489 // Neither l and r are both false.
1490 continue;
1491 }
1492 }
1493
1494 // Either l or r are true.
1496 restartChecks = true;
1497 ++numSelfIntersections;
1498 break;
1499 } // for
1500 } else {
1501 BoolFail bf = this->checkForIntersection(
1502 leftEnclosingEdge, rightEnclosingEdge, &activeEdges, &v, mesh, c);
1503 if (bf == BoolFail::kFail) {
1505 }
1506 if (bf == BoolFail::kTrue) {
1508 restartChecks = true;
1509 ++numSelfIntersections;
1510 }
1511 }
1512 } while (restartChecks);
1513#ifdef SK_DEBUG
1514 validate_edge_list(&activeEdges, c);
1515#endif
1516 for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) {
1517 if (!activeEdges.remove(e)) {
1519 }
1520 }
1521 Edge* leftEdge = leftEnclosingEdge;
1522 for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) {
1523 activeEdges.insert(e, leftEdge);
1524 leftEdge = e;
1525 }
1526 }
1527 SkASSERT(!activeEdges.fHead && !activeEdges.fTail);
1528 return result;
1529}
1530
1531// Stage 5: Tessellate the simplified mesh into monotone polygons.
1532
1533std::tuple<Poly*, bool> GrTriangulator::tessellate(const VertexList& vertices, const Comparator&) {
1534 TESS_LOG("\ntessellating simple polygons\n");
1535 EdgeList activeEdges;
1536 Poly* polys = nullptr;
1537 for (Vertex* v = vertices.fHead; v != nullptr; v = v->fNext) {
1538 if (!v->isConnected()) {
1539 continue;
1540 }
1541#if TRIANGULATOR_LOGGING
1542 TESS_LOG("\nvertex %g: (%g,%g), alpha %d\n", v->fID, v->fPoint.fX, v->fPoint.fY, v->fAlpha);
1543#endif
1544 Edge* leftEnclosingEdge;
1545 Edge* rightEnclosingEdge;
1546 FindEnclosingEdges(*v, activeEdges, &leftEnclosingEdge, &rightEnclosingEdge);
1547 Poly* leftPoly;
1548 Poly* rightPoly;
1549 if (v->fFirstEdgeAbove) {
1550 leftPoly = v->fFirstEdgeAbove->fLeftPoly;
1551 rightPoly = v->fLastEdgeAbove->fRightPoly;
1552 } else {
1553 leftPoly = leftEnclosingEdge ? leftEnclosingEdge->fRightPoly : nullptr;
1554 rightPoly = rightEnclosingEdge ? rightEnclosingEdge->fLeftPoly : nullptr;
1555 }
1556#if TRIANGULATOR_LOGGING
1557 TESS_LOG("edges above:\n");
1558 for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) {
1559 TESS_LOG("%g -> %g, lpoly %d, rpoly %d\n",
1560 e->fTop->fID, e->fBottom->fID,
1561 e->fLeftPoly ? e->fLeftPoly->fID : -1,
1562 e->fRightPoly ? e->fRightPoly->fID : -1);
1563 }
1564 TESS_LOG("edges below:\n");
1565 for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) {
1566 TESS_LOG("%g -> %g, lpoly %d, rpoly %d\n",
1567 e->fTop->fID, e->fBottom->fID,
1568 e->fLeftPoly ? e->fLeftPoly->fID : -1,
1569 e->fRightPoly ? e->fRightPoly->fID : -1);
1570 }
1571#endif
1572 if (v->fFirstEdgeAbove) {
1573 if (leftPoly) {
1574 leftPoly = leftPoly->addEdge(v->fFirstEdgeAbove, kRight_Side, this);
1575 }
1576 if (rightPoly) {
1577 rightPoly = rightPoly->addEdge(v->fLastEdgeAbove, kLeft_Side, this);
1578 }
1579 for (Edge* e = v->fFirstEdgeAbove; e != v->fLastEdgeAbove; e = e->fNextEdgeAbove) {
1580 Edge* rightEdge = e->fNextEdgeAbove;
1581 activeEdges.remove(e);
1582 if (e->fRightPoly) {
1583 e->fRightPoly->addEdge(e, kLeft_Side, this);
1584 }
1585 if (rightEdge->fLeftPoly && rightEdge->fLeftPoly != e->fRightPoly) {
1586 rightEdge->fLeftPoly->addEdge(e, kRight_Side, this);
1587 }
1588 }
1589 activeEdges.remove(v->fLastEdgeAbove);
1590 if (!v->fFirstEdgeBelow) {
1591 if (leftPoly && rightPoly && leftPoly != rightPoly) {
1592 SkASSERT(leftPoly->fPartner == nullptr && rightPoly->fPartner == nullptr);
1593 rightPoly->fPartner = leftPoly;
1594 leftPoly->fPartner = rightPoly;
1595 }
1596 }
1597 }
1598 if (v->fFirstEdgeBelow) {
1599 if (!v->fFirstEdgeAbove) {
1600 if (leftPoly && rightPoly) {
1601 if (leftPoly == rightPoly) {
1602 if (leftPoly->fTail && leftPoly->fTail->fSide == kLeft_Side) {
1603 leftPoly = this->makePoly(&polys, leftPoly->lastVertex(),
1604 leftPoly->fWinding);
1605 leftEnclosingEdge->fRightPoly = leftPoly;
1606 } else {
1607 rightPoly = this->makePoly(&polys, rightPoly->lastVertex(),
1608 rightPoly->fWinding);
1609 rightEnclosingEdge->fLeftPoly = rightPoly;
1610 }
1611 }
1612 Edge* join = this->allocateEdge(leftPoly->lastVertex(), v, 1, EdgeType::kInner);
1613 leftPoly = leftPoly->addEdge(join, kRight_Side, this);
1614 rightPoly = rightPoly->addEdge(join, kLeft_Side, this);
1615 }
1616 }
1617 Edge* leftEdge = v->fFirstEdgeBelow;
1618 leftEdge->fLeftPoly = leftPoly;
1619 activeEdges.insert(leftEdge, leftEnclosingEdge);
1620 for (Edge* rightEdge = leftEdge->fNextEdgeBelow; rightEdge;
1621 rightEdge = rightEdge->fNextEdgeBelow) {
1622 activeEdges.insert(rightEdge, leftEdge);
1623 int winding = leftEdge->fLeftPoly ? leftEdge->fLeftPoly->fWinding : 0;
1624 winding += leftEdge->fWinding;
1625 if (winding != 0) {
1626 Poly* poly = this->makePoly(&polys, v, winding);
1627 leftEdge->fRightPoly = rightEdge->fLeftPoly = poly;
1628 }
1629 leftEdge = rightEdge;
1630 }
1631 v->fLastEdgeBelow->fRightPoly = rightPoly;
1632 }
1633#if TRIANGULATOR_LOGGING
1634 TESS_LOG("\nactive edges:\n");
1635 for (Edge* e = activeEdges.fHead; e != nullptr; e = e->fRight) {
1636 TESS_LOG("%g -> %g, lpoly %d, rpoly %d\n",
1637 e->fTop->fID, e->fBottom->fID,
1638 e->fLeftPoly ? e->fLeftPoly->fID : -1,
1639 e->fRightPoly ? e->fRightPoly->fID : -1);
1640 }
1641#endif
1642 }
1643 return { polys, true };
1644}
1645
1646// This is a driver function that calls stages 2-5 in turn.
1647
1649 const Comparator& c) {
1650#if TRIANGULATOR_LOGGING
1651 for (int i = 0; i < contourCnt; ++i) {
1652 Vertex* v = contours[i].fHead;
1653 SkASSERT(v);
1654 TESS_LOG("path.moveTo(%20.20g, %20.20g);\n", v->fPoint.fX, v->fPoint.fY);
1655 for (v = v->fNext; v; v = v->fNext) {
1656 TESS_LOG("path.lineTo(%20.20g, %20.20g);\n", v->fPoint.fX, v->fPoint.fY);
1657 }
1658 }
1659#endif
1660 this->sanitizeContours(contours, contourCnt);
1661 this->buildEdges(contours, contourCnt, mesh, c);
1662}
1663
1665 if (!vertices || !vertices->fHead) {
1666 return;
1667 }
1668
1669 // Sort vertices in Y (secondarily in X).
1671 merge_sort<sweep_lt_horiz>(vertices);
1672 } else {
1673 merge_sort<sweep_lt_vert>(vertices);
1674 }
1675#if TRIANGULATOR_LOGGING
1676 for (Vertex* v = vertices->fHead; v != nullptr; v = v->fNext) {
1677 static float gID = 0.0f;
1678 v->fID = gID++;
1679 }
1680#endif
1681}
1682
1683std::tuple<Poly*, bool> GrTriangulator::contoursToPolys(VertexList* contours, int contourCnt) {
1684 const SkRect& pathBounds = fPath.getBounds();
1685 Comparator c(pathBounds.width() > pathBounds.height() ? Comparator::Direction::kHorizontal
1688 this->contoursToMesh(contours, contourCnt, &mesh, c);
1689 TESS_LOG("\ninitial mesh:\n");
1690 DUMP_MESH(mesh);
1691 SortMesh(&mesh, c);
1692 TESS_LOG("\nsorted mesh:\n");
1693 DUMP_MESH(mesh);
1694 this->mergeCoincidentVertices(&mesh, c);
1695 TESS_LOG("\nsorted+merged mesh:\n");
1696 DUMP_MESH(mesh);
1697 auto result = this->simplify(&mesh, c);
1699 return { nullptr, false };
1700 }
1701 TESS_LOG("\nsimplified mesh:\n");
1702 DUMP_MESH(mesh);
1703 return this->tessellate(mesh, c);
1704}
1705
1706// Stage 6: Triangulate the monotone polygons into a vertex buffer.
1708 SkPathFillType overrideFillType,
1709 skgpu::VertexWriter data) const {
1710 for (Poly* poly = polys; poly; poly = poly->fNext) {
1711 if (apply_fill_type(overrideFillType, poly)) {
1712 data = this->emitPoly(poly, std::move(data));
1713 }
1714 }
1715 return data;
1716}
1717
1718static int get_contour_count(const SkPath& path, SkScalar tolerance) {
1719 // We could theoretically be more aggressive about not counting empty contours, but we need to
1720 // actually match the exact number of contour linked lists the tessellator will create later on.
1721 int contourCnt = 1;
1722 bool hasPoints = false;
1723
1724 SkPath::Iter iter(path, false);
1725 SkPath::Verb verb;
1726 SkPoint pts[4];
1727 bool first = true;
1728 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1729 switch (verb) {
1730 case SkPath::kMove_Verb:
1731 if (!first) {
1732 ++contourCnt;
1733 }
1734 [[fallthrough]];
1735 case SkPath::kLine_Verb:
1737 case SkPath::kQuad_Verb:
1739 hasPoints = true;
1740 break;
1741 default:
1742 break;
1743 }
1744 first = false;
1745 }
1746 if (!hasPoints) {
1747 return 0;
1748 }
1749 return contourCnt;
1750}
1751
1752std::tuple<Poly*, bool> GrTriangulator::pathToPolys(float tolerance, const SkRect& clipBounds, bool* isLinear) {
1753 int contourCnt = get_contour_count(fPath, tolerance);
1754 if (contourCnt <= 0) {
1755 *isLinear = true;
1756 return { nullptr, true };
1757 }
1758
1760 contourCnt++;
1761 }
1762 std::unique_ptr<VertexList[]> contours(new VertexList[contourCnt]);
1763
1764 this->pathToContours(tolerance, clipBounds, contours.get(), isLinear);
1765 return this->contoursToPolys(contours.get(), contourCnt);
1766}
1767
1768int64_t GrTriangulator::CountPoints(Poly* polys, SkPathFillType overrideFillType) {
1769 int64_t count = 0;
1770 for (Poly* poly = polys; poly; poly = poly->fNext) {
1771 if (apply_fill_type(overrideFillType, poly) && poly->fCount >= 3) {
1772 count += (poly->fCount - 2) * (TRIANGULATOR_WIREFRAME ? 6 : 3);
1773 }
1774 }
1775 return count;
1776}
1777
1778// Stage 6: Triangulate the monotone polygons into a vertex buffer.
1779
1781 int64_t count64 = CountPoints(polys, fPath.getFillType());
1782 if (0 == count64 || count64 > SK_MaxS32) {
1783 return 0;
1784 }
1785 int count = count64;
1786
1787 size_t vertexStride = sizeof(SkPoint);
1788 if (fEmitCoverage) {
1789 vertexStride += sizeof(float);
1790 }
1791 skgpu::VertexWriter verts = vertexAllocator->lockWriter(vertexStride, count);
1792 if (!verts) {
1793 SkDebugf("Could not allocate vertices\n");
1794 return 0;
1795 }
1796
1797 TESS_LOG("emitting %d verts\n", count);
1798
1800 verts = this->polysToTriangles(polys, fPath.getFillType(), std::move(verts));
1801
1802 int actualCount = static_cast<int>((verts.mark() - start) / vertexStride);
1803 SkASSERT(actualCount <= count);
1804 vertexAllocator->unlock(actualCount);
1805 return actualCount;
1806}
1807
1808#endif // SK_ENABLE_OPTIMIZE_SIZE
Instance * fNext
int count
Definition: FontMgrTest.cpp:50
static float GrNormalizeByteToFloat(uint8_t value)
Definition: GrColor.h:71
static SkPoint clamp(SkPoint p, SkPoint min, SkPoint max, const Comparator &c)
static bool rewind_if_necessary(Edge *edge, EdgeList *activeEdges, Vertex **current, const Comparator &c)
static void list_insert(T *t, T *prev, T *next, T **head, T **tail)
static int get_contour_count(const SkPath &path, SkScalar tolerance)
GrTriangulator::VertexList VertexList
static bool rewind(EdgeList *activeEdges, Vertex **current, Vertex *dst, const Comparator &c)
static bool bottom_collinear(Edge *left, Edge *right)
static SkScalar quad_error_at(const SkPoint pts[3], SkScalar t, SkScalar u)
bool(* CompareFunc)(const SkPoint &a, const SkPoint &b)
static bool recursive_edge_intersect(const Line &u, SkPoint u0, SkPoint u1, const Line &v, SkPoint v0, SkPoint v1, SkPoint *p, double *s, double *t)
static void remove_edge_above(Edge *edge)
GrTriangulator::Comparator Comparator
#define DUMP_MESH(M)
static bool coincident(const SkPoint &a, const SkPoint &b)
static void list_remove(T *t, T **head, T **tail)
GrTriangulator::MonotonePoly MonotonePoly
GrTriangulator::Vertex Vertex
static bool edge_line_needs_recursion(const SkPoint &p0, const SkPoint &p1)
GrTriangulator::Line Line
GrTriangulator::Edge Edge
static bool top_collinear(Edge *left, Edge *right)
static void merge_sort(VertexList *vertices)
static void round(SkPoint *p)
static skgpu::VertexWriter emit_vertex(Vertex *v, bool emitCoverage, skgpu::VertexWriter data)
#define TESS_LOG(...)
GrTriangulator::EdgeList EdgeList
static skgpu::VertexWriter emit_triangle(Vertex *v0, Vertex *v1, Vertex *v2, bool emitCoverage, skgpu::VertexWriter data)
static void sorted_merge(VertexList *front, VertexList *back, VertexList *result)
static SkScalar double_to_clamped_scalar(double d)
GrTriangulator::Poly Poly
static void remove_edge_below(Edge *edge)
static bool sweep_lt_horiz(const SkPoint &a, const SkPoint &b)
static bool apply_fill_type(SkPathFillType fillType, int winding)
static bool sweep_lt_vert(const SkPoint &a, const SkPoint &b)
#define TRIANGULATOR_WIREFRAME
static float next(float f)
static float prev(float f)
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static bool SkIsFinite(T x, Pack... values)
static constexpr int32_t SK_MaxS32
Definition: SkMath.h:21
static int side(double x)
static bool SkPathFillType_IsInverse(SkPathFillType ft)
Definition: SkPathTypes.h:26
SkPathFillType
Definition: SkPathTypes.h:11
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition: SkRefCnt.h:341
#define SK_ScalarMax
Definition: SkScalar.h:24
#define SkScalarAve(a, b)
Definition: SkScalar.h:74
#define SkDoubleToScalar(x)
Definition: SkScalar.h:64
#define SkScalarRoundToScalar(x)
Definition: SkScalar.h:32
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition: SkTPin.h:19
static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv)
Definition: SkYUVMath.cpp:629
Vec2Value v2
GLenum type
skgpu::VertexWriter lockWriter(size_t stride, int eagerCount)
virtual void unlock(int actualCount)=0
void append(SkArenaAlloc *alloc, SkPoint a, SkPoint b, SkPoint c, int winding)
void contoursToMesh(VertexList *contours, int contourCnt, VertexList *mesh, const Comparator &)
std::tuple< Poly *, bool > pathToPolys(float tolerance, const SkRect &clipBounds, bool *isLinear)
skgpu::VertexWriter emitMonotonePoly(const MonotonePoly *, skgpu::VertexWriter data) const
static int64_t CountPoints(Poly *polys, SkPathFillType overrideFillType)
static void SortMesh(VertexList *vertices, const Comparator &)
bool mergeCollinearEdges(Edge *edge, EdgeList *activeEdges, Vertex **current, const Comparator &) const
Edge * allocateEdge(Vertex *top, Vertex *bottom, int winding, EdgeType type)
bool setTop(Edge *edge, Vertex *v, EdgeList *activeEdges, Vertex **current, const Comparator &) const
bool fPreserveCollinearVertices
Edge * makeEdge(Vertex *prev, Vertex *next, EdgeType type, const Comparator &)
SkArenaAlloc *const fAlloc
void buildEdges(VertexList *contours, int contourCnt, VertexList *mesh, const Comparator &)
bool fCollectBreadcrumbTriangles
BreadcrumbTriangleList fBreadcrumbList
bool applyFillType(int winding) const
MonotonePoly * allocateMonotonePoly(Edge *edge, Side side, int winding)
SimplifyResult simplify(VertexList *mesh, const Comparator &)
virtual std::tuple< Poly *, bool > tessellate(const VertexList &vertices, const Comparator &)
Poly * makePoly(Poly **head, Vertex *v, int winding) const
skgpu::VertexWriter polysToTriangles(Poly *polys, SkPathFillType overrideFillType, skgpu::VertexWriter data) const
static void SortedMerge(VertexList *front, VertexList *back, VertexList *result, const Comparator &)
bool setBottom(Edge *edge, Vertex *v, EdgeList *activeEdges, Vertex **current, const Comparator &) const
bool fRoundVerticesToQuarterPixel
void pathToContours(float tolerance, const SkRect &clipBounds, VertexList *contours, bool *isLinear) const
Edge * makeConnectingEdge(Vertex *prev, Vertex *next, EdgeType, const Comparator &, int windingScale=1)
void mergeVertices(Vertex *src, Vertex *dst, VertexList *mesh, const Comparator &) const
const SkPath fPath
Vertex * makeSortedVertex(const SkPoint &, uint8_t alpha, VertexList *mesh, Vertex *reference, const Comparator &) const
skgpu::VertexWriter emitPoly(const Poly *, skgpu::VertexWriter data) const
bool mergeEdgesAbove(Edge *edge, Edge *other, EdgeList *activeEdges, Vertex **current, const Comparator &) const
void generateCubicPoints(const SkPoint &, const SkPoint &, const SkPoint &, const SkPoint &, SkScalar tolSqd, VertexList *contour, int pointsLeft) const
static void FindEnclosingEdges(const Vertex &v, const EdgeList &edges, Edge **left, Edge **right)
std::tuple< Poly *, bool > contoursToPolys(VertexList *contours, int contourCnt)
skgpu::VertexWriter emitTriangle(Vertex *prev, Vertex *curr, Vertex *next, int winding, skgpu::VertexWriter data) const
bool mergeCoincidentVertices(VertexList *mesh, const Comparator &) const
bool mergeEdgesBelow(Edge *edge, Edge *other, EdgeList *activeEdges, Vertex **current, const Comparator &) const
void sanitizeContours(VertexList *contours, int contourCnt) const
void appendQuadraticToContour(const SkPoint[3], SkScalar toleranceSqd, VertexList *contour) const
BoolFail intersectEdgePair(Edge *left, Edge *right, EdgeList *activeEdges, Vertex **current, const Comparator &)
BoolFail checkForIntersection(Edge *left, Edge *right, EdgeList *activeEdges, Vertex **current, VertexList *mesh, const Comparator &)
void appendPointToContour(const SkPoint &p, VertexList *contour) const
BoolFail splitEdge(Edge *edge, Vertex *v, EdgeList *activeEdges, Vertex **current, const Comparator &)
void computeBisector(Edge *edge1, Edge *edge2, Vertex *) const
auto make(Ctor &&ctor) -> decltype(ctor(nullptr))
Definition: SkArenaAlloc.h:120
Verb next(SkPoint pts[4])
Definition: SkPath.cpp:1901
SkScalar conicWeight() const
Definition: SkPath.h:1535
Definition: SkPath.h:59
bool isInverseFillType() const
Definition: SkPath.h:244
SkPathFillType getFillType() const
Definition: SkPath.h:230
const SkRect & getBounds() const
Definition: SkPath.cpp:430
@ kClose_Verb
Definition: SkPath.h:1471
@ kMove_Verb
Definition: SkPath.h:1466
@ kConic_Verb
Definition: SkPath.h:1469
@ kDone_Verb
Definition: SkPath.h:1472
@ kCubic_Verb
Definition: SkPath.h:1470
@ kQuad_Verb
Definition: SkPath.h:1468
@ kLine_Verb
Definition: SkPath.h:1467
static SkScalar DistanceToLineSegmentBetweenSqd(const SkPoint &pt, const SkPoint &a, const SkPoint &b)
Definition: SkPoint.cpp:126
static SkPoint to_point(SkIPoint p)
Definition: editor.cpp:75
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct s
struct MyStruct a[10]
GAsyncResult * result
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
uint32_t cubicPointCount(const SkPoint points[], SkScalar tol)
static const int kMaxPointsPerCurve
Definition: GrPathUtils.h:35
SkMesh mesh
Definition: SkRecords.h:345
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
Definition: ab.py:1
string converter
Definition: cacheimages.py:19
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
dst
Definition: cp.py:12
constexpr bool contains(std::string_view str, std::string_view needle)
Definition: SkStringView.h:41
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition: SkVx.h:707
#define T
Definition: precompiler.cc:65
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741
const Scalar scale
bool sweep_lt(const SkPoint &a, const SkPoint &b) const
void insert(Edge *edge, Edge *prev, Edge *next)
bool contains(Edge *edge) const
void insertBelow(Vertex *, const Comparator &)
void insertAbove(Vertex *, const Comparator &)
bool intersect(const Edge &other, SkPoint *p, uint8_t *alpha=nullptr) const
double dist(const SkPoint &p) const
bool intersect(const Line &other, SkPoint *point) const
Poly(Vertex *v, int winding)
MonotonePoly * fTail
Vertex * lastVertex() const
MonotonePoly * fHead
Poly * addEdge(Edge *e, Side side, GrTriangulator *)
void insert(Vertex *v, Vertex *prev, Vertex *next)
float fX
x-axis value
Definition: SkPoint_impl.h:164
bool isFinite() const
Definition: SkPoint_impl.h:412
float fY
y-axis value
Definition: SkPoint_impl.h:165
void toQuad(SkPoint quad[4]) const
Definition: SkRect.cpp:50
constexpr float height() const
Definition: SkRect.h:769
constexpr float width() const
Definition: SkRect.h:762
Mark mark(size_t offset=0) const
Definition: BufferWriter.h:58
Definition: SkVx.h:83
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63