blob: b921db33c1fe9607acf10d2ea9ccf687f7afa6a2 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2011 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 */
reed@android.com909994f2009-11-18 16:09:51 +00007#include "SkEdgeBuilder.h"
8#include "SkPath.h"
9#include "SkEdge.h"
liyuqian38911a72016-10-04 11:23:22 -070010#include "SkAnalyticEdge.h"
reed@android.com909994f2009-11-18 16:09:51 +000011#include "SkEdgeClipper.h"
12#include "SkLineClipper.h"
13#include "SkGeometry.h"
14
reed@android.com909994f2009-11-18 16:09:51 +000015///////////////////////////////////////////////////////////////////////////////
16
Cary Clarkcd0a61e2017-04-14 14:29:33 -040017SkEdgeBuilder::SkEdgeBuilder() {
Mike Klein2b46f3e2017-03-02 22:04:55 +000018 fEdgeList = nullptr;
19}
reed@google.comc8d640b2012-08-02 14:26:43 +000020
caryclarkafd25f72016-01-30 14:07:20 -080021SkEdgeBuilder::Combine SkEdgeBuilder::CombineVertical(const SkEdge* edge, SkEdge* last) {
22 if (last->fCurveCount || last->fDX || edge->fX != last->fX) {
23 return kNo_Combine;
24 }
25 if (edge->fWinding == last->fWinding) {
26 if (edge->fLastY + 1 == last->fFirstY) {
27 last->fFirstY = edge->fFirstY;
28 return kPartial_Combine;
29 }
30 if (edge->fFirstY == last->fLastY + 1) {
31 last->fLastY = edge->fLastY;
32 return kPartial_Combine;
33 }
34 return kNo_Combine;
35 }
36 if (edge->fFirstY == last->fFirstY) {
37 if (edge->fLastY == last->fLastY) {
38 return kTotal_Combine;
39 }
40 if (edge->fLastY < last->fLastY) {
41 last->fFirstY = edge->fLastY + 1;
42 return kPartial_Combine;
43 }
44 last->fFirstY = last->fLastY + 1;
45 last->fLastY = edge->fLastY;
46 last->fWinding = edge->fWinding;
47 return kPartial_Combine;
48 }
49 if (edge->fLastY == last->fLastY) {
50 if (edge->fFirstY > last->fFirstY) {
51 last->fLastY = edge->fFirstY - 1;
52 return kPartial_Combine;
53 }
54 last->fLastY = last->fFirstY - 1;
55 last->fFirstY = edge->fFirstY;
56 last->fWinding = edge->fWinding;
57 return kPartial_Combine;
58 }
59 return kNo_Combine;
60}
61
liyuqian451ceba2016-11-07 08:10:44 -080062static inline bool approximatelyEqual(SkFixed a, SkFixed b) {
63 return SkAbs32(a - b) < 0x100;
64}
65
liyuqian38911a72016-10-04 11:23:22 -070066SkEdgeBuilder::Combine SkEdgeBuilder::CombineVertical(
67 const SkAnalyticEdge* edge, SkAnalyticEdge* last) {
Yuqian Lidf60e362017-07-25 11:26:31 -040068 SkASSERT(fEdgeType == kAnalyticEdge);
liyuqian38911a72016-10-04 11:23:22 -070069 if (last->fCurveCount || last->fDX || edge->fX != last->fX) {
70 return kNo_Combine;
71 }
72 if (edge->fWinding == last->fWinding) {
73 if (edge->fLowerY == last->fUpperY) {
74 last->fUpperY = edge->fUpperY;
75 last->fY = last->fUpperY;
76 return kPartial_Combine;
77 }
liyuqian451ceba2016-11-07 08:10:44 -080078 if (approximatelyEqual(edge->fUpperY, last->fLowerY)) {
liyuqian38911a72016-10-04 11:23:22 -070079 last->fLowerY = edge->fLowerY;
80 return kPartial_Combine;
81 }
82 return kNo_Combine;
83 }
liyuqian451ceba2016-11-07 08:10:44 -080084 if (approximatelyEqual(edge->fUpperY, last->fUpperY)) {
85 if (approximatelyEqual(edge->fLowerY, last->fLowerY)) {
liyuqian38911a72016-10-04 11:23:22 -070086 return kTotal_Combine;
87 }
88 if (edge->fLowerY < last->fLowerY) {
89 last->fUpperY = edge->fLowerY;
90 last->fY = last->fUpperY;
91 return kPartial_Combine;
92 }
93 last->fUpperY = last->fLowerY;
94 last->fY = last->fUpperY;
95 last->fLowerY = edge->fLowerY;
96 last->fWinding = edge->fWinding;
97 return kPartial_Combine;
98 }
liyuqian451ceba2016-11-07 08:10:44 -080099 if (approximatelyEqual(edge->fLowerY, last->fLowerY)) {
liyuqian38911a72016-10-04 11:23:22 -0700100 if (edge->fUpperY > last->fUpperY) {
101 last->fLowerY = edge->fUpperY;
102 return kPartial_Combine;
103 }
104 last->fLowerY = last->fUpperY;
105 last->fUpperY = edge->fUpperY;
106 last->fY = last->fUpperY;
107 last->fWinding = edge->fWinding;
108 return kPartial_Combine;
109 }
110 return kNo_Combine;
111}
112
113bool SkEdgeBuilder::vertical_line(const SkEdge* edge) {
114 return !edge->fDX && !edge->fCurveCount;
115}
116
117bool SkEdgeBuilder::vertical_line(const SkAnalyticEdge* edge) {
Yuqian Lidf60e362017-07-25 11:26:31 -0400118 SkASSERT(fEdgeType == kAnalyticEdge);
caryclarkafd25f72016-01-30 14:07:20 -0800119 return !edge->fDX && !edge->fCurveCount;
120}
121
reed@android.com909994f2009-11-18 16:09:51 +0000122void SkEdgeBuilder::addLine(const SkPoint pts[]) {
Yuqian Lidf60e362017-07-25 11:26:31 -0400123 if (fEdgeType == kBezier) {
124 SkLine* line = fAlloc.make<SkLine>();
125 if (line->set(pts)) {
126 fList.push(line);
127 }
128 } else if (fEdgeType == kAnalyticEdge) {
Herb Derby4b1b04d2017-03-01 13:44:39 -0500129 SkAnalyticEdge* edge = fAlloc.make<SkAnalyticEdge>();
liyuqian38911a72016-10-04 11:23:22 -0700130 if (edge->setLine(pts[0], pts[1])) {
131 if (vertical_line(edge) && fList.count()) {
132 Combine combine = CombineVertical(edge, (SkAnalyticEdge*)*(fList.end() - 1));
133 if (kNo_Combine != combine) {
134 if (kTotal_Combine == combine) {
135 fList.pop();
136 }
137 goto unallocate_analytic_edge;
caryclarkafd25f72016-01-30 14:07:20 -0800138 }
caryclarkafd25f72016-01-30 14:07:20 -0800139 }
liyuqian38911a72016-10-04 11:23:22 -0700140 fList.push(edge);
141 } else {
142unallocate_analytic_edge:
143 ;
144 // TODO: unallocate edge from storage...
caryclarkafd25f72016-01-30 14:07:20 -0800145 }
reed@android.com909994f2009-11-18 16:09:51 +0000146 } else {
Herb Derby4b1b04d2017-03-01 13:44:39 -0500147 SkEdge* edge = fAlloc.make<SkEdge>();
liyuqian38911a72016-10-04 11:23:22 -0700148 if (edge->setLine(pts[0], pts[1], fShiftUp)) {
149 if (vertical_line(edge) && fList.count()) {
150 Combine combine = CombineVertical(edge, (SkEdge*)*(fList.end() - 1));
151 if (kNo_Combine != combine) {
152 if (kTotal_Combine == combine) {
153 fList.pop();
154 }
155 goto unallocate_edge;
156 }
157 }
158 fList.push(edge);
159 } else {
caryclarkafd25f72016-01-30 14:07:20 -0800160unallocate_edge:
liyuqian38911a72016-10-04 11:23:22 -0700161 ;
162 // TODO: unallocate edge from storage...
163 }
reed@android.com909994f2009-11-18 16:09:51 +0000164 }
165}
166
167void SkEdgeBuilder::addQuad(const SkPoint pts[]) {
Yuqian Lidf60e362017-07-25 11:26:31 -0400168 if (fEdgeType == kBezier) {
169 SkQuad* quad = fAlloc.make<SkQuad>();
170 if (quad->set(pts)) {
171 fList.push(quad);
172 }
173 } else if (fEdgeType == kAnalyticEdge) {
Herb Derby4b1b04d2017-03-01 13:44:39 -0500174 SkAnalyticQuadraticEdge* edge = fAlloc.make<SkAnalyticQuadraticEdge>();
liyuqian38911a72016-10-04 11:23:22 -0700175 if (edge->setQuadratic(pts)) {
176 fList.push(edge);
177 } else {
178 // TODO: unallocate edge from storage...
179 }
reed@android.com909994f2009-11-18 16:09:51 +0000180 } else {
Herb Derby4b1b04d2017-03-01 13:44:39 -0500181 SkQuadraticEdge* edge = fAlloc.make<SkQuadraticEdge>();
liyuqian38911a72016-10-04 11:23:22 -0700182 if (edge->setQuadratic(pts, fShiftUp)) {
183 fList.push(edge);
184 } else {
185 // TODO: unallocate edge from storage...
186 }
reed@android.com909994f2009-11-18 16:09:51 +0000187 }
188}
189
190void SkEdgeBuilder::addCubic(const SkPoint pts[]) {
Yuqian Lidf60e362017-07-25 11:26:31 -0400191 if (fEdgeType == kBezier) {
192 SkCubic* cubic = fAlloc.make<SkCubic>();
193 if (cubic->set(pts)) {
194 fList.push(cubic);
195 }
196 } else if (fEdgeType == kAnalyticEdge) {
Herb Derby4b1b04d2017-03-01 13:44:39 -0500197 SkAnalyticCubicEdge* edge = fAlloc.make<SkAnalyticCubicEdge>();
liyuqian38911a72016-10-04 11:23:22 -0700198 if (edge->setCubic(pts)) {
199 fList.push(edge);
200 } else {
201 // TODO: unallocate edge from storage...
202 }
reed@android.com909994f2009-11-18 16:09:51 +0000203 } else {
Herb Derby4b1b04d2017-03-01 13:44:39 -0500204 SkCubicEdge* edge = fAlloc.make<SkCubicEdge>();
liyuqian38911a72016-10-04 11:23:22 -0700205 if (edge->setCubic(pts, fShiftUp)) {
206 fList.push(edge);
207 } else {
208 // TODO: unallocate edge from storage...
209 }
reed@android.com909994f2009-11-18 16:09:51 +0000210 }
211}
212
213void SkEdgeBuilder::addClipper(SkEdgeClipper* clipper) {
214 SkPoint pts[4];
215 SkPath::Verb verb;
216
217 while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
218 switch (verb) {
219 case SkPath::kLine_Verb:
220 this->addLine(pts);
221 break;
222 case SkPath::kQuad_Verb:
223 this->addQuad(pts);
224 break;
225 case SkPath::kCubic_Verb:
226 this->addCubic(pts);
227 break;
228 default:
229 break;
230 }
231 }
232}
233
234///////////////////////////////////////////////////////////////////////////////
235
236static void setShiftedClip(SkRect* dst, const SkIRect& src, int shift) {
237 dst->set(SkIntToScalar(src.fLeft >> shift),
238 SkIntToScalar(src.fTop >> shift),
239 SkIntToScalar(src.fRight >> shift),
240 SkIntToScalar(src.fBottom >> shift));
241}
242
caryclarkafd25f72016-01-30 14:07:20 -0800243SkEdgeBuilder::Combine SkEdgeBuilder::checkVertical(const SkEdge* edge, SkEdge** edgePtr) {
liyuqian38911a72016-10-04 11:23:22 -0700244 return !vertical_line(edge) || edgePtr <= (SkEdge**)fEdgeList ? kNo_Combine :
245 CombineVertical(edge, edgePtr[-1]);
246}
247
248SkEdgeBuilder::Combine SkEdgeBuilder::checkVertical(const SkAnalyticEdge* edge,
249 SkAnalyticEdge** edgePtr) {
Yuqian Lidf60e362017-07-25 11:26:31 -0400250 SkASSERT(fEdgeType == kAnalyticEdge);
liyuqian38911a72016-10-04 11:23:22 -0700251 return !vertical_line(edge) || edgePtr <= (SkAnalyticEdge**)fEdgeList ? kNo_Combine :
caryclarkafd25f72016-01-30 14:07:20 -0800252 CombineVertical(edge, edgePtr[-1]);
253}
254
reed01d33192015-02-07 12:18:41 -0800255int SkEdgeBuilder::buildPoly(const SkPath& path, const SkIRect* iclip, int shiftUp,
reed31223e02015-02-09 08:33:07 -0800256 bool canCullToTheRight) {
reed@google.comc8d640b2012-08-02 14:26:43 +0000257 SkPath::Iter iter(path, true);
258 SkPoint pts[4];
259 SkPath::Verb verb;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000260
reed@google.comc8d640b2012-08-02 14:26:43 +0000261 int maxEdgeCount = path.countPoints();
262 if (iclip) {
263 // clipping can turn 1 line into (up to) kMaxClippedLineSegments, since
264 // we turn portions that are clipped out on the left/right into vertical
265 // segments.
266 maxEdgeCount *= SkLineClipper::kMaxClippedLineSegments;
267 }
reed@google.comc8d640b2012-08-02 14:26:43 +0000268
Yuqian Lidf60e362017-07-25 11:26:31 -0400269 size_t edgeSize;
270 char* edge;
271 switch (fEdgeType) {
272 case kEdge:
273 edgeSize = sizeof(SkEdge);
274 edge = (char*)fAlloc.makeArrayDefault<SkEdge>(maxEdgeCount);
275 break;
276 case kAnalyticEdge:
277 edgeSize = sizeof(SkAnalyticEdge);
278 edge = (char*)fAlloc.makeArrayDefault<SkAnalyticEdge>(maxEdgeCount);
279 break;
280 case kBezier:
281 edgeSize = sizeof(SkLine);
282 edge = (char*)fAlloc.makeArrayDefault<SkLine>(maxEdgeCount);
283 break;
284 }
Herb Derby4b1b04d2017-03-01 13:44:39 -0500285
286 SkDEBUGCODE(char* edgeStart = edge);
287 char** edgePtr = fAlloc.makeArrayDefault<char*>(maxEdgeCount);
liyuqian38911a72016-10-04 11:23:22 -0700288 fEdgeList = (void**)edgePtr;
reed@google.comc8d640b2012-08-02 14:26:43 +0000289
290 if (iclip) {
291 SkRect clip;
292 setShiftedClip(&clip, *iclip, shiftUp);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000293
reed@google.comc8d640b2012-08-02 14:26:43 +0000294 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
295 switch (verb) {
296 case SkPath::kMove_Verb:
297 case SkPath::kClose_Verb:
298 // we ignore these, and just get the whole segment from
299 // the corresponding line/quad/cubic verbs
300 break;
301 case SkPath::kLine_Verb: {
302 SkPoint lines[SkLineClipper::kMaxPoints];
reed31223e02015-02-09 08:33:07 -0800303 int lineCount = SkLineClipper::ClipLine(pts, clip, lines, canCullToTheRight);
reed@google.comc8d640b2012-08-02 14:26:43 +0000304 SkASSERT(lineCount <= SkLineClipper::kMaxClippedLineSegments);
305 for (int i = 0; i < lineCount; i++) {
Yuqian Lidf60e362017-07-25 11:26:31 -0400306 this->addPolyLine(lines + i, edge, edgeSize, edgePtr, shiftUp);
reed@google.comc8d640b2012-08-02 14:26:43 +0000307 }
308 break;
309 }
310 default:
311 SkDEBUGFAIL("unexpected verb");
312 break;
313 }
314 }
315 } else {
316 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
317 switch (verb) {
318 case SkPath::kMove_Verb:
319 case SkPath::kClose_Verb:
320 // we ignore these, and just get the whole segment from
321 // the corresponding line/quad/cubic verbs
322 break;
liyuqian38911a72016-10-04 11:23:22 -0700323 case SkPath::kLine_Verb: {
Yuqian Lidf60e362017-07-25 11:26:31 -0400324 this->addPolyLine(pts, edge, edgeSize, edgePtr, shiftUp);
reed@google.comc8d640b2012-08-02 14:26:43 +0000325 break;
liyuqian38911a72016-10-04 11:23:22 -0700326 }
reed@google.comc8d640b2012-08-02 14:26:43 +0000327 default:
328 SkDEBUGFAIL("unexpected verb");
329 break;
330 }
331 }
332 }
Herb Derby4b1b04d2017-03-01 13:44:39 -0500333 SkASSERT((size_t)(edge - edgeStart) <= maxEdgeCount * edgeSize);
liyuqian38911a72016-10-04 11:23:22 -0700334 SkASSERT(edgePtr - (char**)fEdgeList <= maxEdgeCount);
335 return SkToInt(edgePtr - (char**)fEdgeList);
reed@google.comc8d640b2012-08-02 14:26:43 +0000336}
337
reed@google.com277c3f82013-05-31 15:17:50 +0000338static void handle_quad(SkEdgeBuilder* builder, const SkPoint pts[3]) {
339 SkPoint monoX[5];
340 int n = SkChopQuadAtYExtrema(pts, monoX);
341 for (int i = 0; i <= n; i++) {
342 builder->addQuad(&monoX[i * 2]);
343 }
344}
345
reed01d33192015-02-07 12:18:41 -0800346int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip, int shiftUp,
Yuqian Lidf60e362017-07-25 11:26:31 -0400347 bool canCullToTheRight, EdgeType edgeType) {
reed@android.com909994f2009-11-18 16:09:51 +0000348 fAlloc.reset();
349 fList.reset();
350 fShiftUp = shiftUp;
Yuqian Lidf60e362017-07-25 11:26:31 -0400351 fEdgeType = edgeType;
reed@android.com909994f2009-11-18 16:09:51 +0000352
reed@google.comc8d640b2012-08-02 14:26:43 +0000353 if (SkPath::kLine_SegmentMask == path.getSegmentMasks()) {
reed31223e02015-02-09 08:33:07 -0800354 return this->buildPoly(path, iclip, shiftUp, canCullToTheRight);
reed@google.comc8d640b2012-08-02 14:26:43 +0000355 }
356
reed220f9262014-12-17 08:21:04 -0800357 SkAutoConicToQuads quadder;
reed3f4e0452015-01-06 07:44:21 -0800358 const SkScalar conicTol = SK_Scalar1 / 4;
reed220f9262014-12-17 08:21:04 -0800359
reed@android.com909994f2009-11-18 16:09:51 +0000360 SkPath::Iter iter(path, true);
361 SkPoint pts[4];
362 SkPath::Verb verb;
363
364 if (iclip) {
365 SkRect clip;
366 setShiftedClip(&clip, *iclip, shiftUp);
reed31223e02015-02-09 08:33:07 -0800367 SkEdgeClipper clipper(canCullToTheRight);
reed@android.com909994f2009-11-18 16:09:51 +0000368
reed@google.com4a3b7142012-05-16 17:16:46 +0000369 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
reed@android.com909994f2009-11-18 16:09:51 +0000370 switch (verb) {
371 case SkPath::kMove_Verb:
372 case SkPath::kClose_Verb:
373 // we ignore these, and just get the whole segment from
374 // the corresponding line/quad/cubic verbs
375 break;
Mike Reed5baafa82017-01-26 14:21:26 -0500376 case SkPath::kLine_Verb:
377 if (clipper.clipLine(pts[0], pts[1], clip)) {
378 this->addClipper(&clipper);
reed@android.com909994f2009-11-18 16:09:51 +0000379 }
380 break;
reed@android.com909994f2009-11-18 16:09:51 +0000381 case SkPath::kQuad_Verb:
382 if (clipper.clipQuad(pts, clip)) {
383 this->addClipper(&clipper);
384 }
385 break;
reed@google.com277c3f82013-05-31 15:17:50 +0000386 case SkPath::kConic_Verb: {
reed220f9262014-12-17 08:21:04 -0800387 const SkPoint* quadPts = quadder.computeQuads(
388 pts, iter.conicWeight(), conicTol);
389 for (int i = 0; i < quadder.countQuads(); ++i) {
390 if (clipper.clipQuad(quadPts, clip)) {
reed@google.com277c3f82013-05-31 15:17:50 +0000391 this->addClipper(&clipper);
392 }
reed220f9262014-12-17 08:21:04 -0800393 quadPts += 2;
reed@google.com277c3f82013-05-31 15:17:50 +0000394 }
395 } break;
reed@android.com909994f2009-11-18 16:09:51 +0000396 case SkPath::kCubic_Verb:
397 if (clipper.clipCubic(pts, clip)) {
398 this->addClipper(&clipper);
399 }
400 break;
401 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000402 SkDEBUGFAIL("unexpected verb");
reed@android.com909994f2009-11-18 16:09:51 +0000403 break;
404 }
405 }
406 } else {
reed@google.com4a3b7142012-05-16 17:16:46 +0000407 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
reed@android.com909994f2009-11-18 16:09:51 +0000408 switch (verb) {
409 case SkPath::kMove_Verb:
410 case SkPath::kClose_Verb:
411 // we ignore these, and just get the whole segment from
412 // the corresponding line/quad/cubic verbs
413 break;
414 case SkPath::kLine_Verb:
415 this->addLine(pts);
416 break;
417 case SkPath::kQuad_Verb: {
reed@google.com277c3f82013-05-31 15:17:50 +0000418 handle_quad(this, pts);
reed@android.com909994f2009-11-18 16:09:51 +0000419 break;
420 }
reed@google.com277c3f82013-05-31 15:17:50 +0000421 case SkPath::kConic_Verb: {
reed220f9262014-12-17 08:21:04 -0800422 const SkPoint* quadPts = quadder.computeQuads(
423 pts, iter.conicWeight(), conicTol);
424 for (int i = 0; i < quadder.countQuads(); ++i) {
425 handle_quad(this, quadPts);
426 quadPts += 2;
reed@google.com277c3f82013-05-31 15:17:50 +0000427 }
428 } break;
reed@android.com909994f2009-11-18 16:09:51 +0000429 case SkPath::kCubic_Verb: {
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400430 if (fEdgeType == kBezier) {
431 this->addCubic(pts);
432 break;
433 }
reed@android.com909994f2009-11-18 16:09:51 +0000434 SkPoint monoY[10];
435 int n = SkChopCubicAtYExtrema(pts, monoY);
436 for (int i = 0; i <= n; i++) {
437 this->addCubic(&monoY[i * 3]);
438 }
439 break;
440 }
441 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000442 SkDEBUGFAIL("unexpected verb");
reed@android.com909994f2009-11-18 16:09:51 +0000443 break;
444 }
445 }
446 }
reed@google.comc8d640b2012-08-02 14:26:43 +0000447 fEdgeList = fList.begin();
reed@android.com909994f2009-11-18 16:09:51 +0000448 return fList.count();
449}
Yuqian Li1c840882017-05-26 14:50:35 -0400450
451int SkEdgeBuilder::build_edges(const SkPath& path, const SkIRect* shiftedClip,
Yuqian Lidf60e362017-07-25 11:26:31 -0400452 int shiftEdgesUp, bool pathContainedInClip, EdgeType edgeType) {
Yuqian Li1c840882017-05-26 14:50:35 -0400453 // If we're convex, then we need both edges, even the right edge is past the clip
454 const bool canCullToTheRight = !path.isConvex();
455
456 const SkIRect* builderClip = pathContainedInClip ? nullptr : shiftedClip;
Yuqian Lidf60e362017-07-25 11:26:31 -0400457 int count = this->build(path, builderClip, shiftEdgesUp, canCullToTheRight, edgeType);
Yuqian Li1c840882017-05-26 14:50:35 -0400458 SkASSERT(count >= 0);
459
Yuqian Lia81b6262017-09-06 17:10:05 -0400460 // canCullToRight == false should imply count != 1 if edgeType != kBezier.
461 // If edgeType == kBezier (DAA), we don't chop edges at y extrema so count == 1 is valid.
462 // For example, a single cubic edge with a valley shape \_/ is fine for DAA.
463 SkASSERT(edgeType == kBezier || canCullToTheRight || count != 1);
Yuqian Li1c840882017-05-26 14:50:35 -0400464
465 return count;
466}