blob: 927d9ae132b957765ee079e3553b871680d8cae2 [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2 * Copyright 2012 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 */
Mike Kleinc0bd9f92019-04-23 12:05:21 -05007#include "src/core/SkGeometry.h"
Chris Dalton957189b2020-05-07 12:47:26 -06008#include "src/core/SkPathPriv.h"
John Stilesdf078002020-07-14 09:44:57 -04009#include "src/core/SkTSort.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/pathops/SkOpEdgeBuilder.h"
11#include "src/pathops/SkReduceOrder.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000012
13void SkOpEdgeBuilder::init() {
caryclark@google.com07393ca2013-04-08 11:47:37 +000014 fOperand = false;
Mike Reed7d34dc72019-11-26 12:17:17 -050015 fXorMask[0] = fXorMask[1] = ((int)fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
caryclark@google.com07393ca2013-04-08 11:47:37 +000016 : kWinding_PathOpsMask;
caryclark@google.com66560ca2013-04-26 19:51:16 +000017 fUnparseable = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +000018 fSecondHalf = preFetch();
19}
20
caryclark27c015d2016-09-23 05:47:20 -070021// very tiny points cause numerical instability : don't allow them
Chris Dalton1fc1bd32020-05-06 11:44:51 -060022static SkPoint force_small_to_zero(const SkPoint& pt) {
23 SkPoint ret = pt;
24 if (SkScalarAbs(ret.fX) < FLT_EPSILON_ORDERABLE_ERR) {
25 ret.fX = 0;
caryclark27c015d2016-09-23 05:47:20 -070026 }
Chris Dalton1fc1bd32020-05-06 11:44:51 -060027 if (SkScalarAbs(ret.fY) < FLT_EPSILON_ORDERABLE_ERR) {
28 ret.fY = 0;
caryclark27c015d2016-09-23 05:47:20 -070029 }
Chris Dalton1fc1bd32020-05-06 11:44:51 -060030 return ret;
caryclark27c015d2016-09-23 05:47:20 -070031}
32
33static bool can_add_curve(SkPath::Verb verb, SkPoint* curve) {
34 if (SkPath::kMove_Verb == verb) {
35 return false;
36 }
caryclarkcc093722016-09-23 09:32:26 -070037 for (int index = 0; index <= SkPathOpsVerbToPoints(verb); ++index) {
Chris Dalton1fc1bd32020-05-06 11:44:51 -060038 curve[index] = force_small_to_zero(curve[index]);
caryclark27c015d2016-09-23 05:47:20 -070039 }
40 return SkPath::kLine_Verb != verb || !SkDPoint::ApproximatelyEqual(curve[0], curve[1]);
41}
42
caryclark@google.com07393ca2013-04-08 11:47:37 +000043void SkOpEdgeBuilder::addOperand(const SkPath& path) {
44 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
caryclark54359292015-03-26 07:52:43 -070045 fPathVerbs.pop();
caryclark@google.com07393ca2013-04-08 11:47:37 +000046 fPath = &path;
Mike Reed7d34dc72019-11-26 12:17:17 -050047 fXorMask[1] = ((int)fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
caryclark@google.com07393ca2013-04-08 11:47:37 +000048 : kWinding_PathOpsMask;
49 preFetch();
50}
51
caryclark55888e42016-07-18 10:01:36 -070052bool SkOpEdgeBuilder::finish() {
caryclark54359292015-03-26 07:52:43 -070053 fOperand = false;
caryclark55888e42016-07-18 10:01:36 -070054 if (fUnparseable || !walk()) {
caryclark@google.com66560ca2013-04-26 19:51:16 +000055 return false;
56 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000057 complete();
Cary Clarkff114282016-12-14 11:56:16 -050058 SkOpContour* contour = fContourBuilder.contour();
59 if (contour && !contour->count()) {
60 fContoursHead->remove(contour);
caryclark@google.com07393ca2013-04-08 11:47:37 +000061 }
caryclark@google.com66560ca2013-04-26 19:51:16 +000062 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +000063}
64
caryclark@google.com07e97fc2013-07-08 17:17:02 +000065void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +000066 if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) {
caryclark54359292015-03-26 07:52:43 -070067 *fPathVerbs.append() = SkPath::kLine_Verb;
68 *fPathPts.append() = curveStart;
caryclark@google.com07e97fc2013-07-08 17:17:02 +000069 } else {
Cary Clarkb9ae5372016-10-05 10:40:07 -040070 int verbCount = fPathVerbs.count();
71 int ptsCount = fPathPts.count();
72 if (SkPath::kLine_Verb == fPathVerbs[verbCount - 1]
73 && fPathPts[ptsCount - 2] == curveStart) {
74 fPathVerbs.pop();
75 fPathPts.pop();
76 } else {
77 fPathPts[ptsCount - 1] = curveStart;
78 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +000079 }
caryclark54359292015-03-26 07:52:43 -070080 *fPathVerbs.append() = SkPath::kClose_Verb;
caryclark@google.com07e97fc2013-07-08 17:17:02 +000081}
82
caryclark@google.com07393ca2013-04-08 11:47:37 +000083int SkOpEdgeBuilder::preFetch() {
caryclark@google.com66560ca2013-04-26 19:51:16 +000084 if (!fPath->isFinite()) {
85 fUnparseable = true;
86 return 0;
87 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +000088 SkPoint curveStart;
89 SkPoint curve[4];
caryclark@google.com07e97fc2013-07-08 17:17:02 +000090 bool lastCurve = false;
Chris Dalton957189b2020-05-07 12:47:26 -060091 for (auto [pathVerb, pts, w] : SkPathPriv::Iterate(*fPath)) {
92 auto verb = static_cast<SkPath::Verb>(pathVerb);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000093 switch (verb) {
94 case SkPath::kMove_Verb:
95 if (!fAllowOpenContours && lastCurve) {
96 closeContour(curve[0], curveStart);
97 }
caryclark54359292015-03-26 07:52:43 -070098 *fPathVerbs.append() = verb;
Chris Dalton1fc1bd32020-05-06 11:44:51 -060099 curve[0] = force_small_to_zero(pts[0]);
100 *fPathPts.append() = curve[0];
101 curveStart = curve[0];
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000102 lastCurve = false;
103 continue;
104 case SkPath::kLine_Verb:
Chris Dalton1fc1bd32020-05-06 11:44:51 -0600105 curve[1] = force_small_to_zero(pts[1]);
106 if (SkDPoint::ApproximatelyEqual(curve[0], curve[1])) {
caryclark54359292015-03-26 07:52:43 -0700107 uint8_t lastVerb = fPathVerbs.top();
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000108 if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) {
Chris Dalton1fc1bd32020-05-06 11:44:51 -0600109 fPathPts.top() = curve[0] = curve[1];
caryclark@google.com570863f2013-09-16 15:55:01 +0000110 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000111 continue; // skip degenerate points
112 }
113 break;
114 case SkPath::kQuad_Verb:
Chris Dalton1fc1bd32020-05-06 11:44:51 -0600115 curve[1] = force_small_to_zero(pts[1]);
116 curve[2] = force_small_to_zero(pts[2]);
117 verb = SkReduceOrder::Quad(curve, curve);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000118 if (verb == SkPath::kMove_Verb) {
119 continue; // skip degenerate points
120 }
121 break;
caryclark1049f122015-04-20 08:31:59 -0700122 case SkPath::kConic_Verb:
Chris Dalton1fc1bd32020-05-06 11:44:51 -0600123 curve[1] = force_small_to_zero(pts[1]);
124 curve[2] = force_small_to_zero(pts[2]);
125 verb = SkReduceOrder::Quad(curve, curve);
Chris Dalton957189b2020-05-07 12:47:26 -0600126 if (SkPath::kQuad_Verb == verb && 1 != *w) {
caryclark27c015d2016-09-23 05:47:20 -0700127 verb = SkPath::kConic_Verb;
128 } else if (verb == SkPath::kMove_Verb) {
caryclark1049f122015-04-20 08:31:59 -0700129 continue; // skip degenerate points
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000130 }
caryclark1049f122015-04-20 08:31:59 -0700131 break;
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000132 case SkPath::kCubic_Verb:
Chris Dalton1fc1bd32020-05-06 11:44:51 -0600133 curve[1] = force_small_to_zero(pts[1]);
134 curve[2] = force_small_to_zero(pts[2]);
135 curve[3] = force_small_to_zero(pts[3]);
136 verb = SkReduceOrder::Cubic(curve, curve);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000137 if (verb == SkPath::kMove_Verb) {
138 continue; // skip degenerate points
139 }
140 break;
141 case SkPath::kClose_Verb:
142 closeContour(curve[0], curveStart);
143 lastCurve = false;
144 continue;
145 case SkPath::kDone_Verb:
146 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000147 }
caryclark54359292015-03-26 07:52:43 -0700148 *fPathVerbs.append() = verb;
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000149 int ptCount = SkPathOpsVerbToPoints(verb);
Chris Dalton1fc1bd32020-05-06 11:44:51 -0600150 fPathPts.append(ptCount, &curve[1]);
caryclark1049f122015-04-20 08:31:59 -0700151 if (verb == SkPath::kConic_Verb) {
Chris Dalton957189b2020-05-07 12:47:26 -0600152 *fWeights.append() = *w;
caryclark1049f122015-04-20 08:31:59 -0700153 }
Chris Dalton1fc1bd32020-05-06 11:44:51 -0600154 curve[0] = curve[ptCount];
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000155 lastCurve = true;
Chris Dalton957189b2020-05-07 12:47:26 -0600156 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000157 if (!fAllowOpenContours && lastCurve) {
158 closeContour(curve[0], curveStart);
159 }
caryclark54359292015-03-26 07:52:43 -0700160 *fPathVerbs.append() = SkPath::kDone_Verb;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000161 return fPathVerbs.count() - 1;
162}
163
caryclark@google.com66560ca2013-04-26 19:51:16 +0000164bool SkOpEdgeBuilder::close() {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000165 complete();
166 return true;
167}
168
caryclark55888e42016-07-18 10:01:36 -0700169bool SkOpEdgeBuilder::walk() {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000170 uint8_t* verbPtr = fPathVerbs.begin();
171 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
Cary Clark79aa2f12018-05-31 16:22:02 -0400172 SkPoint* pointsPtr = fPathPts.begin();
caryclark1049f122015-04-20 08:31:59 -0700173 SkScalar* weightPtr = fWeights.begin();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000174 SkPath::Verb verb;
Cary Clarkff114282016-12-14 11:56:16 -0500175 SkOpContour* contour = fContourBuilder.contour();
Cary Clark79aa2f12018-05-31 16:22:02 -0400176 int moveToPtrBump = 0;
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000177 while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
178 if (verbPtr == endOfFirstHalf) {
179 fOperand = true;
180 }
181 verbPtr++;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000182 switch (verb) {
183 case SkPath::kMove_Verb:
Cary Clarkff114282016-12-14 11:56:16 -0500184 if (contour && contour->count()) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000185 if (fAllowOpenContours) {
186 complete();
187 } else if (!close()) {
188 return false;
189 }
190 }
Cary Clarkff114282016-12-14 11:56:16 -0500191 if (!contour) {
192 fContourBuilder.setContour(contour = fContoursHead->appendContour());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000193 }
Cary Clarkff114282016-12-14 11:56:16 -0500194 contour->init(fGlobalState, fOperand,
caryclark54359292015-03-26 07:52:43 -0700195 fXorMask[fOperand] == kEvenOdd_PathOpsMask);
Cary Clark79aa2f12018-05-31 16:22:02 -0400196 pointsPtr += moveToPtrBump;
197 moveToPtrBump = 1;
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000198 continue;
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000199 case SkPath::kLine_Verb:
Cary Clarkff114282016-12-14 11:56:16 -0500200 fContourBuilder.addLine(pointsPtr);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000201 break;
202 case SkPath::kQuad_Verb:
caryclark27c015d2016-09-23 05:47:20 -0700203 {
John Stiles14f8d792021-08-10 16:22:22 -0400204 SkVector vec1 = pointsPtr[1] - pointsPtr[0];
205 SkVector vec2 = pointsPtr[2] - pointsPtr[1];
206 if (vec1.dot(vec2) < 0) {
caryclark27c015d2016-09-23 05:47:20 -0700207 SkPoint pair[5];
208 if (SkChopQuadAtMaxCurvature(pointsPtr, pair) == 1) {
209 goto addOneQuad;
210 }
211 if (!SkScalarsAreFinite(&pair[0].fX, SK_ARRAY_COUNT(pair) * 2)) {
212 return false;
213 }
Cary Clarkafca4d62017-12-01 15:23:00 -0500214 for (unsigned index = 0; index < SK_ARRAY_COUNT(pair); ++index) {
Chris Dalton1fc1bd32020-05-06 11:44:51 -0600215 pair[index] = force_small_to_zero(pair[index]);
Cary Clarkafca4d62017-12-01 15:23:00 -0500216 }
caryclark27c015d2016-09-23 05:47:20 -0700217 SkPoint cStorage[2][2];
218 SkPath::Verb v1 = SkReduceOrder::Quad(&pair[0], cStorage[0]);
219 SkPath::Verb v2 = SkReduceOrder::Quad(&pair[2], cStorage[1]);
caryclarkcc093722016-09-23 09:32:26 -0700220 SkPoint* curve1 = v1 != SkPath::kLine_Verb ? &pair[0] : cStorage[0];
221 SkPoint* curve2 = v2 != SkPath::kLine_Verb ? &pair[2] : cStorage[1];
caryclark27c015d2016-09-23 05:47:20 -0700222 if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) {
Cary Clarkff114282016-12-14 11:56:16 -0500223 fContourBuilder.addCurve(v1, curve1);
224 fContourBuilder.addCurve(v2, curve2);
caryclark27c015d2016-09-23 05:47:20 -0700225 break;
226 }
227 }
228 }
229 addOneQuad:
Cary Clarkff114282016-12-14 11:56:16 -0500230 fContourBuilder.addQuad(pointsPtr);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000231 break;
caryclark27c015d2016-09-23 05:47:20 -0700232 case SkPath::kConic_Verb: {
John Stiles14f8d792021-08-10 16:22:22 -0400233 SkVector vec1 = pointsPtr[1] - pointsPtr[0];
234 SkVector vec2 = pointsPtr[2] - pointsPtr[1];
caryclark27c015d2016-09-23 05:47:20 -0700235 SkScalar weight = *weightPtr++;
John Stiles14f8d792021-08-10 16:22:22 -0400236 if (vec1.dot(vec2) < 0) {
caryclark27c015d2016-09-23 05:47:20 -0700237 // FIXME: max curvature for conics hasn't been implemented; use placeholder
238 SkScalar maxCurvature = SkFindQuadMaxCurvature(pointsPtr);
Chris Dalton1d474dd2018-07-24 01:08:31 -0600239 if (0 < maxCurvature && maxCurvature < 1) {
caryclark27c015d2016-09-23 05:47:20 -0700240 SkConic conic(pointsPtr, weight);
241 SkConic pair[2];
caryclark414c4292016-09-26 11:03:54 -0700242 if (!conic.chopAt(maxCurvature, pair)) {
243 // if result can't be computed, use original
Cary Clarkff114282016-12-14 11:56:16 -0500244 fContourBuilder.addConic(pointsPtr, weight);
caryclark414c4292016-09-26 11:03:54 -0700245 break;
246 }
caryclark27c015d2016-09-23 05:47:20 -0700247 SkPoint cStorage[2][3];
248 SkPath::Verb v1 = SkReduceOrder::Conic(pair[0], cStorage[0]);
249 SkPath::Verb v2 = SkReduceOrder::Conic(pair[1], cStorage[1]);
caryclarkcc093722016-09-23 09:32:26 -0700250 SkPoint* curve1 = v1 != SkPath::kLine_Verb ? pair[0].fPts : cStorage[0];
251 SkPoint* curve2 = v2 != SkPath::kLine_Verb ? pair[1].fPts : cStorage[1];
caryclark27c015d2016-09-23 05:47:20 -0700252 if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) {
Cary Clarkff114282016-12-14 11:56:16 -0500253 fContourBuilder.addCurve(v1, curve1, pair[0].fW);
254 fContourBuilder.addCurve(v2, curve2, pair[1].fW);
caryclark27c015d2016-09-23 05:47:20 -0700255 break;
256 }
caryclark1049f122015-04-20 08:31:59 -0700257 }
caryclark54359292015-03-26 07:52:43 -0700258 }
Cary Clarkff114282016-12-14 11:56:16 -0500259 fContourBuilder.addConic(pointsPtr, weight);
caryclark54359292015-03-26 07:52:43 -0700260 } break;
caryclark27c015d2016-09-23 05:47:20 -0700261 case SkPath::kCubic_Verb:
262 {
263 // Split complex cubics (such as self-intersecting curves or
264 // ones with difficult curvature) in two before proceeding.
265 // This can be required for intersection to succeed.
Cary Clark7eb01e02016-12-08 14:36:32 -0500266 SkScalar splitT[3];
267 int breaks = SkDCubic::ComplexBreak(pointsPtr, splitT);
268 if (!breaks) {
Cary Clarkff114282016-12-14 11:56:16 -0500269 fContourBuilder.addCubic(pointsPtr);
Cary Clark7eb01e02016-12-08 14:36:32 -0500270 break;
271 }
Cary Clarkff114282016-12-14 11:56:16 -0500272 SkASSERT(breaks <= (int) SK_ARRAY_COUNT(splitT));
273 struct Splitsville {
274 double fT[2];
275 SkPoint fPts[4];
276 SkPoint fReduced[4];
277 SkPath::Verb fVerb;
278 bool fCanAdd;
Cary Clark0eb6ed42016-12-16 16:31:11 -0500279 } splits[4];
280 SkASSERT(SK_ARRAY_COUNT(splits) == SK_ARRAY_COUNT(splitT) + 1);
John Stiles886a9042020-07-14 16:28:33 -0400281 SkTQSort(splitT, splitT + breaks);
Cary Clark7eb01e02016-12-08 14:36:32 -0500282 for (int index = 0; index <= breaks; ++index) {
Cary Clarkff114282016-12-14 11:56:16 -0500283 Splitsville* split = &splits[index];
284 split->fT[0] = index ? splitT[index - 1] : 0;
285 split->fT[1] = index < breaks ? splitT[index] : 1;
286 SkDCubic part = SkDCubic::SubDivide(pointsPtr, split->fT[0], split->fT[1]);
287 if (!part.toFloatPoints(split->fPts)) {
caryclark27c015d2016-09-23 05:47:20 -0700288 return false;
289 }
Cary Clarkff114282016-12-14 11:56:16 -0500290 split->fVerb = SkReduceOrder::Cubic(split->fPts, split->fReduced);
Kevin Lubick67c905c2020-04-21 12:20:38 -0400291 SkPoint* curve = SkPath::kCubic_Verb == split->fVerb
Cary Clarkff114282016-12-14 11:56:16 -0500292 ? split->fPts : split->fReduced;
293 split->fCanAdd = can_add_curve(split->fVerb, curve);
294 }
295 for (int index = 0; index <= breaks; ++index) {
296 Splitsville* split = &splits[index];
297 if (!split->fCanAdd) {
298 continue;
299 }
300 int prior = index;
301 while (prior > 0 && !splits[prior - 1].fCanAdd) {
302 --prior;
303 }
304 if (prior < index) {
305 split->fT[0] = splits[prior].fT[0];
Cary Clark4efcb7d2018-02-02 15:09:49 -0500306 split->fPts[0] = splits[prior].fPts[0];
Cary Clarkff114282016-12-14 11:56:16 -0500307 }
308 int next = index;
Brian Osman788b9162020-02-07 10:36:46 -0500309 int breakLimit = std::min(breaks, (int) SK_ARRAY_COUNT(splits) - 1);
Eric Boren746e2632017-06-21 13:39:32 -0400310 while (next < breakLimit && !splits[next + 1].fCanAdd) {
Cary Clarkff114282016-12-14 11:56:16 -0500311 ++next;
312 }
313 if (next > index) {
314 split->fT[1] = splits[next].fT[1];
Cary Clark4efcb7d2018-02-02 15:09:49 -0500315 split->fPts[3] = splits[next].fPts[3];
Cary Clarkff114282016-12-14 11:56:16 -0500316 }
317 if (prior < index || next > index) {
Cary Clarkff114282016-12-14 11:56:16 -0500318 split->fVerb = SkReduceOrder::Cubic(split->fPts, split->fReduced);
319 }
320 SkPoint* curve = SkPath::kCubic_Verb == split->fVerb
321 ? split->fPts : split->fReduced;
Cary Clark74b42902018-03-09 07:38:47 -0500322 if (!can_add_curve(split->fVerb, curve)) {
323 return false;
324 }
Cary Clarkff114282016-12-14 11:56:16 -0500325 fContourBuilder.addCurve(split->fVerb, curve);
caryclark27c015d2016-09-23 05:47:20 -0700326 }
327 }
caryclark27c015d2016-09-23 05:47:20 -0700328 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000329 case SkPath::kClose_Verb:
Cary Clarkff114282016-12-14 11:56:16 -0500330 SkASSERT(contour);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000331 if (!close()) {
332 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000333 }
Cary Clarkff114282016-12-14 11:56:16 -0500334 contour = nullptr;
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000335 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000336 default:
337 SkDEBUGFAIL("bad verb");
caryclark@google.com66560ca2013-04-26 19:51:16 +0000338 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000339 }
Cary Clarkff114282016-12-14 11:56:16 -0500340 SkASSERT(contour);
341 if (contour->count()) {
342 contour->debugValidate();
343 }
caryclark54359292015-03-26 07:52:43 -0700344 pointsPtr += SkPathOpsVerbToPoints(verb);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000345 }
Cary Clarkff114282016-12-14 11:56:16 -0500346 fContourBuilder.flush();
347 if (contour && contour->count() &&!fAllowOpenContours && !close()) {
348 return false;
349 }
350 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000351}