xref: /aosp_15_r20/external/skia/src/pathops/SkOpEdgeBuilder.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2012 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkOpEdgeBuilder.h"
8*c8dee2aaSAndroid Build Coastguard Worker 
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkFloatingPoint.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTSort.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkGeometry.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPathPriv.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkPathOpsCubic.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkPathOpsPoint.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkReduceOrder.h"
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
21*c8dee2aaSAndroid Build Coastguard Worker #include <array>
22*c8dee2aaSAndroid Build Coastguard Worker 
init()23*c8dee2aaSAndroid Build Coastguard Worker void SkOpEdgeBuilder::init() {
24*c8dee2aaSAndroid Build Coastguard Worker     fOperand = false;
25*c8dee2aaSAndroid Build Coastguard Worker     fXorMask[0] = fXorMask[1] = ((int)fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
26*c8dee2aaSAndroid Build Coastguard Worker             : kWinding_PathOpsMask;
27*c8dee2aaSAndroid Build Coastguard Worker     fUnparseable = false;
28*c8dee2aaSAndroid Build Coastguard Worker     fSecondHalf = preFetch();
29*c8dee2aaSAndroid Build Coastguard Worker }
30*c8dee2aaSAndroid Build Coastguard Worker 
31*c8dee2aaSAndroid Build Coastguard Worker // very tiny points cause numerical instability : don't allow them
force_small_to_zero(const SkPoint & pt)32*c8dee2aaSAndroid Build Coastguard Worker static SkPoint force_small_to_zero(const SkPoint& pt) {
33*c8dee2aaSAndroid Build Coastguard Worker     SkPoint ret = pt;
34*c8dee2aaSAndroid Build Coastguard Worker     if (SkScalarAbs(ret.fX) < FLT_EPSILON_ORDERABLE_ERR) {
35*c8dee2aaSAndroid Build Coastguard Worker         ret.fX = 0;
36*c8dee2aaSAndroid Build Coastguard Worker     }
37*c8dee2aaSAndroid Build Coastguard Worker     if (SkScalarAbs(ret.fY) < FLT_EPSILON_ORDERABLE_ERR) {
38*c8dee2aaSAndroid Build Coastguard Worker         ret.fY = 0;
39*c8dee2aaSAndroid Build Coastguard Worker     }
40*c8dee2aaSAndroid Build Coastguard Worker     return ret;
41*c8dee2aaSAndroid Build Coastguard Worker }
42*c8dee2aaSAndroid Build Coastguard Worker 
can_add_curve(SkPath::Verb verb,SkPoint * curve)43*c8dee2aaSAndroid Build Coastguard Worker static bool can_add_curve(SkPath::Verb verb, SkPoint* curve) {
44*c8dee2aaSAndroid Build Coastguard Worker     if (SkPath::kMove_Verb == verb) {
45*c8dee2aaSAndroid Build Coastguard Worker         return false;
46*c8dee2aaSAndroid Build Coastguard Worker     }
47*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index <= SkPathOpsVerbToPoints(verb); ++index) {
48*c8dee2aaSAndroid Build Coastguard Worker         curve[index] = force_small_to_zero(curve[index]);
49*c8dee2aaSAndroid Build Coastguard Worker     }
50*c8dee2aaSAndroid Build Coastguard Worker     return SkPath::kLine_Verb != verb || !SkDPoint::ApproximatelyEqual(curve[0], curve[1]);
51*c8dee2aaSAndroid Build Coastguard Worker }
52*c8dee2aaSAndroid Build Coastguard Worker 
addOperand(const SkPath & path)53*c8dee2aaSAndroid Build Coastguard Worker void SkOpEdgeBuilder::addOperand(const SkPath& path) {
54*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!fPathVerbs.empty() && fPathVerbs.back() == SkPath::kDone_Verb);
55*c8dee2aaSAndroid Build Coastguard Worker     fPathVerbs.pop_back();
56*c8dee2aaSAndroid Build Coastguard Worker     fPath = &path;
57*c8dee2aaSAndroid Build Coastguard Worker     fXorMask[1] = ((int)fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
58*c8dee2aaSAndroid Build Coastguard Worker             : kWinding_PathOpsMask;
59*c8dee2aaSAndroid Build Coastguard Worker     preFetch();
60*c8dee2aaSAndroid Build Coastguard Worker }
61*c8dee2aaSAndroid Build Coastguard Worker 
finish()62*c8dee2aaSAndroid Build Coastguard Worker bool SkOpEdgeBuilder::finish() {
63*c8dee2aaSAndroid Build Coastguard Worker     fOperand = false;
64*c8dee2aaSAndroid Build Coastguard Worker     if (fUnparseable || !walk()) {
65*c8dee2aaSAndroid Build Coastguard Worker         return false;
66*c8dee2aaSAndroid Build Coastguard Worker     }
67*c8dee2aaSAndroid Build Coastguard Worker     complete();
68*c8dee2aaSAndroid Build Coastguard Worker     SkOpContour* contour = fContourBuilder.contour();
69*c8dee2aaSAndroid Build Coastguard Worker     if (contour && !contour->count()) {
70*c8dee2aaSAndroid Build Coastguard Worker         fContoursHead->remove(contour);
71*c8dee2aaSAndroid Build Coastguard Worker     }
72*c8dee2aaSAndroid Build Coastguard Worker     return true;
73*c8dee2aaSAndroid Build Coastguard Worker }
74*c8dee2aaSAndroid Build Coastguard Worker 
closeContour(const SkPoint & curveEnd,const SkPoint & curveStart)75*c8dee2aaSAndroid Build Coastguard Worker void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) {
76*c8dee2aaSAndroid Build Coastguard Worker     if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) {
77*c8dee2aaSAndroid Build Coastguard Worker         *fPathVerbs.append() = SkPath::kLine_Verb;
78*c8dee2aaSAndroid Build Coastguard Worker         *fPathPts.append() = curveStart;
79*c8dee2aaSAndroid Build Coastguard Worker     } else {
80*c8dee2aaSAndroid Build Coastguard Worker         int verbCount = fPathVerbs.size();
81*c8dee2aaSAndroid Build Coastguard Worker         int ptsCount = fPathPts.size();
82*c8dee2aaSAndroid Build Coastguard Worker         if (SkPath::kLine_Verb == fPathVerbs[verbCount - 1]
83*c8dee2aaSAndroid Build Coastguard Worker                 && fPathPts[ptsCount - 2] == curveStart) {
84*c8dee2aaSAndroid Build Coastguard Worker             fPathVerbs.pop_back();
85*c8dee2aaSAndroid Build Coastguard Worker             fPathPts.pop_back();
86*c8dee2aaSAndroid Build Coastguard Worker         } else {
87*c8dee2aaSAndroid Build Coastguard Worker             fPathPts[ptsCount - 1] = curveStart;
88*c8dee2aaSAndroid Build Coastguard Worker         }
89*c8dee2aaSAndroid Build Coastguard Worker     }
90*c8dee2aaSAndroid Build Coastguard Worker     *fPathVerbs.append() = SkPath::kClose_Verb;
91*c8dee2aaSAndroid Build Coastguard Worker }
92*c8dee2aaSAndroid Build Coastguard Worker 
preFetch()93*c8dee2aaSAndroid Build Coastguard Worker int SkOpEdgeBuilder::preFetch() {
94*c8dee2aaSAndroid Build Coastguard Worker     if (!fPath->isFinite()) {
95*c8dee2aaSAndroid Build Coastguard Worker         fUnparseable = true;
96*c8dee2aaSAndroid Build Coastguard Worker         return 0;
97*c8dee2aaSAndroid Build Coastguard Worker     }
98*c8dee2aaSAndroid Build Coastguard Worker     SkPoint curveStart;
99*c8dee2aaSAndroid Build Coastguard Worker     SkPoint curve[4];
100*c8dee2aaSAndroid Build Coastguard Worker     bool lastCurve = false;
101*c8dee2aaSAndroid Build Coastguard Worker     for (auto [pathVerb, pts, w] : SkPathPriv::Iterate(*fPath)) {
102*c8dee2aaSAndroid Build Coastguard Worker         auto verb = static_cast<SkPath::Verb>(pathVerb);
103*c8dee2aaSAndroid Build Coastguard Worker         switch (verb) {
104*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kMove_Verb:
105*c8dee2aaSAndroid Build Coastguard Worker                 if (!fAllowOpenContours && lastCurve) {
106*c8dee2aaSAndroid Build Coastguard Worker                     closeContour(curve[0], curveStart);
107*c8dee2aaSAndroid Build Coastguard Worker                 }
108*c8dee2aaSAndroid Build Coastguard Worker                 *fPathVerbs.append() = verb;
109*c8dee2aaSAndroid Build Coastguard Worker                 curve[0] = force_small_to_zero(pts[0]);
110*c8dee2aaSAndroid Build Coastguard Worker                 *fPathPts.append() = curve[0];
111*c8dee2aaSAndroid Build Coastguard Worker                 curveStart = curve[0];
112*c8dee2aaSAndroid Build Coastguard Worker                 lastCurve = false;
113*c8dee2aaSAndroid Build Coastguard Worker                 continue;
114*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kLine_Verb:
115*c8dee2aaSAndroid Build Coastguard Worker                 curve[1] = force_small_to_zero(pts[1]);
116*c8dee2aaSAndroid Build Coastguard Worker                 if (SkDPoint::ApproximatelyEqual(curve[0], curve[1])) {
117*c8dee2aaSAndroid Build Coastguard Worker                     uint8_t lastVerb = fPathVerbs.back();
118*c8dee2aaSAndroid Build Coastguard Worker                     if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) {
119*c8dee2aaSAndroid Build Coastguard Worker                         fPathPts.back() = curve[0] = curve[1];
120*c8dee2aaSAndroid Build Coastguard Worker                     }
121*c8dee2aaSAndroid Build Coastguard Worker                     continue;  // skip degenerate points
122*c8dee2aaSAndroid Build Coastguard Worker                 }
123*c8dee2aaSAndroid Build Coastguard Worker                 break;
124*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kQuad_Verb:
125*c8dee2aaSAndroid Build Coastguard Worker                 curve[1] = force_small_to_zero(pts[1]);
126*c8dee2aaSAndroid Build Coastguard Worker                 curve[2] = force_small_to_zero(pts[2]);
127*c8dee2aaSAndroid Build Coastguard Worker                 verb = SkReduceOrder::Quad(curve, curve);
128*c8dee2aaSAndroid Build Coastguard Worker                 if (verb == SkPath::kMove_Verb) {
129*c8dee2aaSAndroid Build Coastguard Worker                     continue;  // skip degenerate points
130*c8dee2aaSAndroid Build Coastguard Worker                 }
131*c8dee2aaSAndroid Build Coastguard Worker                 break;
132*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kConic_Verb:
133*c8dee2aaSAndroid Build Coastguard Worker                 curve[1] = force_small_to_zero(pts[1]);
134*c8dee2aaSAndroid Build Coastguard Worker                 curve[2] = force_small_to_zero(pts[2]);
135*c8dee2aaSAndroid Build Coastguard Worker                 verb = SkReduceOrder::Quad(curve, curve);
136*c8dee2aaSAndroid Build Coastguard Worker                 if (SkPath::kQuad_Verb == verb && 1 != *w) {
137*c8dee2aaSAndroid Build Coastguard Worker                   verb = SkPath::kConic_Verb;
138*c8dee2aaSAndroid Build Coastguard Worker                 } else if (verb == SkPath::kMove_Verb) {
139*c8dee2aaSAndroid Build Coastguard Worker                     continue;  // skip degenerate points
140*c8dee2aaSAndroid Build Coastguard Worker                 }
141*c8dee2aaSAndroid Build Coastguard Worker                 break;
142*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kCubic_Verb:
143*c8dee2aaSAndroid Build Coastguard Worker                 curve[1] = force_small_to_zero(pts[1]);
144*c8dee2aaSAndroid Build Coastguard Worker                 curve[2] = force_small_to_zero(pts[2]);
145*c8dee2aaSAndroid Build Coastguard Worker                 curve[3] = force_small_to_zero(pts[3]);
146*c8dee2aaSAndroid Build Coastguard Worker                 verb = SkReduceOrder::Cubic(curve, curve);
147*c8dee2aaSAndroid Build Coastguard Worker                 if (verb == SkPath::kMove_Verb) {
148*c8dee2aaSAndroid Build Coastguard Worker                     continue;  // skip degenerate points
149*c8dee2aaSAndroid Build Coastguard Worker                 }
150*c8dee2aaSAndroid Build Coastguard Worker                 break;
151*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kClose_Verb:
152*c8dee2aaSAndroid Build Coastguard Worker                 closeContour(curve[0], curveStart);
153*c8dee2aaSAndroid Build Coastguard Worker                 lastCurve = false;
154*c8dee2aaSAndroid Build Coastguard Worker                 continue;
155*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kDone_Verb:
156*c8dee2aaSAndroid Build Coastguard Worker                 continue;
157*c8dee2aaSAndroid Build Coastguard Worker         }
158*c8dee2aaSAndroid Build Coastguard Worker         *fPathVerbs.append() = verb;
159*c8dee2aaSAndroid Build Coastguard Worker         int ptCount = SkPathOpsVerbToPoints(verb);
160*c8dee2aaSAndroid Build Coastguard Worker         fPathPts.append(ptCount, &curve[1]);
161*c8dee2aaSAndroid Build Coastguard Worker         if (verb == SkPath::kConic_Verb) {
162*c8dee2aaSAndroid Build Coastguard Worker             *fWeights.append() = *w;
163*c8dee2aaSAndroid Build Coastguard Worker         }
164*c8dee2aaSAndroid Build Coastguard Worker         curve[0] = curve[ptCount];
165*c8dee2aaSAndroid Build Coastguard Worker         lastCurve = true;
166*c8dee2aaSAndroid Build Coastguard Worker     }
167*c8dee2aaSAndroid Build Coastguard Worker     if (!fAllowOpenContours && lastCurve) {
168*c8dee2aaSAndroid Build Coastguard Worker         closeContour(curve[0], curveStart);
169*c8dee2aaSAndroid Build Coastguard Worker     }
170*c8dee2aaSAndroid Build Coastguard Worker     *fPathVerbs.append() = SkPath::kDone_Verb;
171*c8dee2aaSAndroid Build Coastguard Worker     return fPathVerbs.size() - 1;
172*c8dee2aaSAndroid Build Coastguard Worker }
173*c8dee2aaSAndroid Build Coastguard Worker 
close()174*c8dee2aaSAndroid Build Coastguard Worker bool SkOpEdgeBuilder::close() {
175*c8dee2aaSAndroid Build Coastguard Worker     complete();
176*c8dee2aaSAndroid Build Coastguard Worker     return true;
177*c8dee2aaSAndroid Build Coastguard Worker }
178*c8dee2aaSAndroid Build Coastguard Worker 
walk()179*c8dee2aaSAndroid Build Coastguard Worker bool SkOpEdgeBuilder::walk() {
180*c8dee2aaSAndroid Build Coastguard Worker     uint8_t* verbPtr = fPathVerbs.begin();
181*c8dee2aaSAndroid Build Coastguard Worker     uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
182*c8dee2aaSAndroid Build Coastguard Worker     SkPoint* pointsPtr = fPathPts.begin();
183*c8dee2aaSAndroid Build Coastguard Worker     SkScalar* weightPtr = fWeights.begin();
184*c8dee2aaSAndroid Build Coastguard Worker     SkPath::Verb verb;
185*c8dee2aaSAndroid Build Coastguard Worker     SkOpContour* contour = fContourBuilder.contour();
186*c8dee2aaSAndroid Build Coastguard Worker     int moveToPtrBump = 0;
187*c8dee2aaSAndroid Build Coastguard Worker     while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
188*c8dee2aaSAndroid Build Coastguard Worker         if (verbPtr == endOfFirstHalf) {
189*c8dee2aaSAndroid Build Coastguard Worker             fOperand = true;
190*c8dee2aaSAndroid Build Coastguard Worker         }
191*c8dee2aaSAndroid Build Coastguard Worker         verbPtr++;
192*c8dee2aaSAndroid Build Coastguard Worker         switch (verb) {
193*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kMove_Verb:
194*c8dee2aaSAndroid Build Coastguard Worker                 if (contour && contour->count()) {
195*c8dee2aaSAndroid Build Coastguard Worker                     if (fAllowOpenContours) {
196*c8dee2aaSAndroid Build Coastguard Worker                         complete();
197*c8dee2aaSAndroid Build Coastguard Worker                     } else if (!close()) {
198*c8dee2aaSAndroid Build Coastguard Worker                         return false;
199*c8dee2aaSAndroid Build Coastguard Worker                     }
200*c8dee2aaSAndroid Build Coastguard Worker                 }
201*c8dee2aaSAndroid Build Coastguard Worker                 if (!contour) {
202*c8dee2aaSAndroid Build Coastguard Worker                     fContourBuilder.setContour(contour = fContoursHead->appendContour());
203*c8dee2aaSAndroid Build Coastguard Worker                 }
204*c8dee2aaSAndroid Build Coastguard Worker                 contour->init(fGlobalState, fOperand,
205*c8dee2aaSAndroid Build Coastguard Worker                     fXorMask[fOperand] == kEvenOdd_PathOpsMask);
206*c8dee2aaSAndroid Build Coastguard Worker                 pointsPtr += moveToPtrBump;
207*c8dee2aaSAndroid Build Coastguard Worker                 moveToPtrBump = 1;
208*c8dee2aaSAndroid Build Coastguard Worker                 continue;
209*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kLine_Verb:
210*c8dee2aaSAndroid Build Coastguard Worker                 fContourBuilder.addLine(pointsPtr);
211*c8dee2aaSAndroid Build Coastguard Worker                 break;
212*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kQuad_Verb:
213*c8dee2aaSAndroid Build Coastguard Worker                 {
214*c8dee2aaSAndroid Build Coastguard Worker                     SkVector vec1 = pointsPtr[1] - pointsPtr[0];
215*c8dee2aaSAndroid Build Coastguard Worker                     SkVector vec2 = pointsPtr[2] - pointsPtr[1];
216*c8dee2aaSAndroid Build Coastguard Worker                     if (vec1.dot(vec2) < 0) {
217*c8dee2aaSAndroid Build Coastguard Worker                         SkPoint pair[5];
218*c8dee2aaSAndroid Build Coastguard Worker                         if (SkChopQuadAtMaxCurvature(pointsPtr, pair) == 1) {
219*c8dee2aaSAndroid Build Coastguard Worker                             goto addOneQuad;
220*c8dee2aaSAndroid Build Coastguard Worker                         }
221*c8dee2aaSAndroid Build Coastguard Worker                         if (!SkIsFinite(&pair[0].fX, std::size(pair) * 2)) {
222*c8dee2aaSAndroid Build Coastguard Worker                             return false;
223*c8dee2aaSAndroid Build Coastguard Worker                         }
224*c8dee2aaSAndroid Build Coastguard Worker                         for (unsigned index = 0; index < std::size(pair); ++index) {
225*c8dee2aaSAndroid Build Coastguard Worker                             pair[index] = force_small_to_zero(pair[index]);
226*c8dee2aaSAndroid Build Coastguard Worker                         }
227*c8dee2aaSAndroid Build Coastguard Worker                         SkPoint cStorage[2][2];
228*c8dee2aaSAndroid Build Coastguard Worker                         SkPath::Verb v1 = SkReduceOrder::Quad(&pair[0], cStorage[0]);
229*c8dee2aaSAndroid Build Coastguard Worker                         SkPath::Verb v2 = SkReduceOrder::Quad(&pair[2], cStorage[1]);
230*c8dee2aaSAndroid Build Coastguard Worker                         SkPoint* curve1 = v1 != SkPath::kLine_Verb ? &pair[0] : cStorage[0];
231*c8dee2aaSAndroid Build Coastguard Worker                         SkPoint* curve2 = v2 != SkPath::kLine_Verb ? &pair[2] : cStorage[1];
232*c8dee2aaSAndroid Build Coastguard Worker                         if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) {
233*c8dee2aaSAndroid Build Coastguard Worker                             fContourBuilder.addCurve(v1, curve1);
234*c8dee2aaSAndroid Build Coastguard Worker                             fContourBuilder.addCurve(v2, curve2);
235*c8dee2aaSAndroid Build Coastguard Worker                             break;
236*c8dee2aaSAndroid Build Coastguard Worker                         }
237*c8dee2aaSAndroid Build Coastguard Worker                     }
238*c8dee2aaSAndroid Build Coastguard Worker                 }
239*c8dee2aaSAndroid Build Coastguard Worker             addOneQuad:
240*c8dee2aaSAndroid Build Coastguard Worker                 fContourBuilder.addQuad(pointsPtr);
241*c8dee2aaSAndroid Build Coastguard Worker                 break;
242*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kConic_Verb: {
243*c8dee2aaSAndroid Build Coastguard Worker                 SkVector vec1 = pointsPtr[1] - pointsPtr[0];
244*c8dee2aaSAndroid Build Coastguard Worker                 SkVector vec2 = pointsPtr[2] - pointsPtr[1];
245*c8dee2aaSAndroid Build Coastguard Worker                 SkScalar weight = *weightPtr++;
246*c8dee2aaSAndroid Build Coastguard Worker                 if (vec1.dot(vec2) < 0) {
247*c8dee2aaSAndroid Build Coastguard Worker                     // FIXME: max curvature for conics hasn't been implemented; use placeholder
248*c8dee2aaSAndroid Build Coastguard Worker                     SkScalar maxCurvature = SkFindQuadMaxCurvature(pointsPtr);
249*c8dee2aaSAndroid Build Coastguard Worker                     if (0 < maxCurvature && maxCurvature < 1) {
250*c8dee2aaSAndroid Build Coastguard Worker                         SkConic conic(pointsPtr, weight);
251*c8dee2aaSAndroid Build Coastguard Worker                         SkConic pair[2];
252*c8dee2aaSAndroid Build Coastguard Worker                         if (!conic.chopAt(maxCurvature, pair)) {
253*c8dee2aaSAndroid Build Coastguard Worker                             // if result can't be computed, use original
254*c8dee2aaSAndroid Build Coastguard Worker                             fContourBuilder.addConic(pointsPtr, weight);
255*c8dee2aaSAndroid Build Coastguard Worker                             break;
256*c8dee2aaSAndroid Build Coastguard Worker                         }
257*c8dee2aaSAndroid Build Coastguard Worker                         SkPoint cStorage[2][3];
258*c8dee2aaSAndroid Build Coastguard Worker                         SkPath::Verb v1 = SkReduceOrder::Conic(pair[0], cStorage[0]);
259*c8dee2aaSAndroid Build Coastguard Worker                         SkPath::Verb v2 = SkReduceOrder::Conic(pair[1], cStorage[1]);
260*c8dee2aaSAndroid Build Coastguard Worker                         SkPoint* curve1 = v1 != SkPath::kLine_Verb ? pair[0].fPts : cStorage[0];
261*c8dee2aaSAndroid Build Coastguard Worker                         SkPoint* curve2 = v2 != SkPath::kLine_Verb ? pair[1].fPts : cStorage[1];
262*c8dee2aaSAndroid Build Coastguard Worker                         if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) {
263*c8dee2aaSAndroid Build Coastguard Worker                             fContourBuilder.addCurve(v1, curve1, pair[0].fW);
264*c8dee2aaSAndroid Build Coastguard Worker                             fContourBuilder.addCurve(v2, curve2, pair[1].fW);
265*c8dee2aaSAndroid Build Coastguard Worker                             break;
266*c8dee2aaSAndroid Build Coastguard Worker                         }
267*c8dee2aaSAndroid Build Coastguard Worker                     }
268*c8dee2aaSAndroid Build Coastguard Worker                 }
269*c8dee2aaSAndroid Build Coastguard Worker                 fContourBuilder.addConic(pointsPtr, weight);
270*c8dee2aaSAndroid Build Coastguard Worker                 } break;
271*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kCubic_Verb:
272*c8dee2aaSAndroid Build Coastguard Worker                 {
273*c8dee2aaSAndroid Build Coastguard Worker                     // Split complex cubics (such as self-intersecting curves or
274*c8dee2aaSAndroid Build Coastguard Worker                     // ones with difficult curvature) in two before proceeding.
275*c8dee2aaSAndroid Build Coastguard Worker                     // This can be required for intersection to succeed.
276*c8dee2aaSAndroid Build Coastguard Worker                     SkScalar splitT[3];
277*c8dee2aaSAndroid Build Coastguard Worker                     int breaks = SkDCubic::ComplexBreak(pointsPtr, splitT);
278*c8dee2aaSAndroid Build Coastguard Worker                     if (!breaks) {
279*c8dee2aaSAndroid Build Coastguard Worker                         fContourBuilder.addCubic(pointsPtr);
280*c8dee2aaSAndroid Build Coastguard Worker                         break;
281*c8dee2aaSAndroid Build Coastguard Worker                     }
282*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(breaks <= (int) std::size(splitT));
283*c8dee2aaSAndroid Build Coastguard Worker                     struct Splitsville {
284*c8dee2aaSAndroid Build Coastguard Worker                         double fT[2];
285*c8dee2aaSAndroid Build Coastguard Worker                         SkPoint fPts[4];
286*c8dee2aaSAndroid Build Coastguard Worker                         SkPoint fReduced[4];
287*c8dee2aaSAndroid Build Coastguard Worker                         SkPath::Verb fVerb;
288*c8dee2aaSAndroid Build Coastguard Worker                         bool fCanAdd;
289*c8dee2aaSAndroid Build Coastguard Worker                     } splits[4];
290*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(std::size(splits) == std::size(splitT) + 1);
291*c8dee2aaSAndroid Build Coastguard Worker                     SkTQSort(splitT, splitT + breaks);
292*c8dee2aaSAndroid Build Coastguard Worker                     for (int index = 0; index <= breaks; ++index) {
293*c8dee2aaSAndroid Build Coastguard Worker                         Splitsville* split = &splits[index];
294*c8dee2aaSAndroid Build Coastguard Worker                         split->fT[0] = index ? splitT[index - 1] : 0;
295*c8dee2aaSAndroid Build Coastguard Worker                         split->fT[1] = index < breaks ? splitT[index] : 1;
296*c8dee2aaSAndroid Build Coastguard Worker                         SkDCubic part = SkDCubic::SubDivide(pointsPtr, split->fT[0], split->fT[1]);
297*c8dee2aaSAndroid Build Coastguard Worker                         if (!part.toFloatPoints(split->fPts)) {
298*c8dee2aaSAndroid Build Coastguard Worker                             return false;
299*c8dee2aaSAndroid Build Coastguard Worker                         }
300*c8dee2aaSAndroid Build Coastguard Worker                         split->fVerb = SkReduceOrder::Cubic(split->fPts, split->fReduced);
301*c8dee2aaSAndroid Build Coastguard Worker                         SkPoint* curve = SkPath::kCubic_Verb == split->fVerb
302*c8dee2aaSAndroid Build Coastguard Worker                                 ? split->fPts : split->fReduced;
303*c8dee2aaSAndroid Build Coastguard Worker                         split->fCanAdd = can_add_curve(split->fVerb, curve);
304*c8dee2aaSAndroid Build Coastguard Worker                     }
305*c8dee2aaSAndroid Build Coastguard Worker                     for (int index = 0; index <= breaks; ++index) {
306*c8dee2aaSAndroid Build Coastguard Worker                         Splitsville* split = &splits[index];
307*c8dee2aaSAndroid Build Coastguard Worker                         if (!split->fCanAdd) {
308*c8dee2aaSAndroid Build Coastguard Worker                             continue;
309*c8dee2aaSAndroid Build Coastguard Worker                         }
310*c8dee2aaSAndroid Build Coastguard Worker                         int prior = index;
311*c8dee2aaSAndroid Build Coastguard Worker                         while (prior > 0 && !splits[prior - 1].fCanAdd) {
312*c8dee2aaSAndroid Build Coastguard Worker                             --prior;
313*c8dee2aaSAndroid Build Coastguard Worker                         }
314*c8dee2aaSAndroid Build Coastguard Worker                         if (prior < index) {
315*c8dee2aaSAndroid Build Coastguard Worker                             split->fT[0] = splits[prior].fT[0];
316*c8dee2aaSAndroid Build Coastguard Worker                             split->fPts[0] = splits[prior].fPts[0];
317*c8dee2aaSAndroid Build Coastguard Worker                         }
318*c8dee2aaSAndroid Build Coastguard Worker                         int next = index;
319*c8dee2aaSAndroid Build Coastguard Worker                         int breakLimit = std::min(breaks, (int) std::size(splits) - 1);
320*c8dee2aaSAndroid Build Coastguard Worker                         while (next < breakLimit && !splits[next + 1].fCanAdd) {
321*c8dee2aaSAndroid Build Coastguard Worker                             ++next;
322*c8dee2aaSAndroid Build Coastguard Worker                         }
323*c8dee2aaSAndroid Build Coastguard Worker                         if (next > index) {
324*c8dee2aaSAndroid Build Coastguard Worker                             split->fT[1] = splits[next].fT[1];
325*c8dee2aaSAndroid Build Coastguard Worker                             split->fPts[3] = splits[next].fPts[3];
326*c8dee2aaSAndroid Build Coastguard Worker                         }
327*c8dee2aaSAndroid Build Coastguard Worker                         if (prior < index || next > index) {
328*c8dee2aaSAndroid Build Coastguard Worker                             split->fVerb = SkReduceOrder::Cubic(split->fPts, split->fReduced);
329*c8dee2aaSAndroid Build Coastguard Worker                         }
330*c8dee2aaSAndroid Build Coastguard Worker                         SkPoint* curve = SkPath::kCubic_Verb == split->fVerb
331*c8dee2aaSAndroid Build Coastguard Worker                                 ? split->fPts : split->fReduced;
332*c8dee2aaSAndroid Build Coastguard Worker                         if (!can_add_curve(split->fVerb, curve)) {
333*c8dee2aaSAndroid Build Coastguard Worker                             return false;
334*c8dee2aaSAndroid Build Coastguard Worker                         }
335*c8dee2aaSAndroid Build Coastguard Worker                         fContourBuilder.addCurve(split->fVerb, curve);
336*c8dee2aaSAndroid Build Coastguard Worker                     }
337*c8dee2aaSAndroid Build Coastguard Worker                 }
338*c8dee2aaSAndroid Build Coastguard Worker                 break;
339*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kClose_Verb:
340*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(contour);
341*c8dee2aaSAndroid Build Coastguard Worker                 if (!close()) {
342*c8dee2aaSAndroid Build Coastguard Worker                     return false;
343*c8dee2aaSAndroid Build Coastguard Worker                 }
344*c8dee2aaSAndroid Build Coastguard Worker                 contour = nullptr;
345*c8dee2aaSAndroid Build Coastguard Worker                 continue;
346*c8dee2aaSAndroid Build Coastguard Worker             default:
347*c8dee2aaSAndroid Build Coastguard Worker                 SkDEBUGFAIL("bad verb");
348*c8dee2aaSAndroid Build Coastguard Worker                 return false;
349*c8dee2aaSAndroid Build Coastguard Worker         }
350*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(contour);
351*c8dee2aaSAndroid Build Coastguard Worker         if (contour->count()) {
352*c8dee2aaSAndroid Build Coastguard Worker             contour->debugValidate();
353*c8dee2aaSAndroid Build Coastguard Worker         }
354*c8dee2aaSAndroid Build Coastguard Worker         pointsPtr += SkPathOpsVerbToPoints(verb);
355*c8dee2aaSAndroid Build Coastguard Worker     }
356*c8dee2aaSAndroid Build Coastguard Worker     fContourBuilder.flush();
357*c8dee2aaSAndroid Build Coastguard Worker     if (contour && contour->count() &&!fAllowOpenContours && !close()) {
358*c8dee2aaSAndroid Build Coastguard Worker         return false;
359*c8dee2aaSAndroid Build Coastguard Worker     }
360*c8dee2aaSAndroid Build Coastguard Worker     return true;
361*c8dee2aaSAndroid Build Coastguard Worker }
362