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