blob: 59ce914e236621ec5420a412bf77eae15a4dd638 [file] [log] [blame]
caryclark@google.comc6825902012-02-03 22:07:47 +00001
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
caryclark@google.com6680fb12012-02-07 22:10:51 +00009#include "CurveIntersection.h"
caryclark@google.comc6825902012-02-03 22:07:47 +000010#include "LineIntersection.h"
11#include "SkPath.h"
12#include "SkRect.h"
13#include "SkTArray.h"
14#include "SkTDArray.h"
caryclark@google.comd88e0892012-03-27 13:23:51 +000015#include "ShapeOps.h"
caryclark@google.comc6825902012-02-03 22:07:47 +000016#include "TSearch.h"
17
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000018#if 0 // set to 1 for no debugging whatsoever
caryclark@google.comd88e0892012-03-27 13:23:51 +000019const bool gShowDebugf = false; // FIXME: remove once debugging is complete
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000020
21#define DEBUG_DUMP 0
22#define DEBUG_ADD 0
23#define DEBUG_ADD_INTERSECTING_TS 0
24#define DEBUG_ADD_BOTTOM_TS 0
25#define COMPARE_DOUBLE 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000026#define DEBUG_ABOVE_BELOW 0
27#define DEBUG_ACTIVE_LESS_THAN 0
28#define DEBUG_SORT_HORIZONTAL 0
29#define DEBUG_OUT 0
30#define DEBUG_OUT_LESS_THAN 0
31#define DEBUG_ADJUST_COINCIDENT 0
caryclark@google.com752b60e2012-03-22 21:11:17 +000032#define DEBUG_BOTTOM 0
33
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000034#else
caryclark@google.comd88e0892012-03-27 13:23:51 +000035const bool gShowDebugf = true; // FIXME: remove once debugging is complete
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000036
37#define DEBUG_DUMP 01
38#define DEBUG_ADD 01
39#define DEBUG_ADD_INTERSECTING_TS 0
40#define DEBUG_ADD_BOTTOM_TS 0
caryclark@google.comd88e0892012-03-27 13:23:51 +000041#define COMPARE_DOUBLE 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000042#define DEBUG_ABOVE_BELOW 01
caryclark@google.com752b60e2012-03-22 21:11:17 +000043#define DEBUG_ACTIVE_LESS_THAN 01
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000044#define DEBUG_SORT_HORIZONTAL 01
45#define DEBUG_OUT 01
46#define DEBUG_OUT_LESS_THAN 0
47#define DEBUG_ADJUST_COINCIDENT 1
caryclark@google.com752b60e2012-03-22 21:11:17 +000048#define DEBUG_BOTTOM 01
49
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000050#endif
51
caryclark@google.comd88e0892012-03-27 13:23:51 +000052// FIXME: not wild about this -- for SkScalars backed by floats, would like to
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000053// represent deltas in terms of number of significant matching bits
54#define MIN_PT_DELTA 0.000001
caryclark@google.comc17972e2012-02-20 21:33:22 +000055
caryclark@google.com6680fb12012-02-07 22:10:51 +000056static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
caryclark@google.comc6825902012-02-03 22:07:47 +000057 double aRange[2], double bRange[2]) {
58 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
59 _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
60 return intersect(aLine, bLine, aRange, bRange);
61}
62
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000063static int LineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
64 SkScalar y, double aRange[2]) {
caryclark@google.comc6825902012-02-03 22:07:47 +000065 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000066 return horizontalLineIntersect(aLine, left, right, y, aRange);
caryclark@google.comc6825902012-02-03 22:07:47 +000067}
68
caryclark@google.comcd4421d2012-03-01 19:16:31 +000069static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +000070 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.comcd4421d2012-03-01 19:16:31 +000071 double x, y;
72 xy_at_t(aLine, t, x, y);
73 out->fX = SkDoubleToScalar(x);
74 out->fY = SkDoubleToScalar(y);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +000075}
76
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000077#if COMPARE_DOUBLE
78static void LineXYAtT(const SkPoint a[2], double t, _Point* out) {
79 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
80 xy_at_t(aLine, t, out->x, out->y);
81}
82#endif
83
84#if 0 // unused for now
85static SkScalar LineXAtT(const SkPoint a[2], double t) {
86 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
87 double x;
88 xy_at_t(aLine, t, x, *(double*) 0);
89 return SkDoubleToScalar(x);
90}
91#endif
92
caryclark@google.com6680fb12012-02-07 22:10:51 +000093static SkScalar LineYAtT(const SkPoint a[2], double t) {
94 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
95 double y;
96 xy_at_t(aLine, t, *(double*) 0, y);
97 return SkDoubleToScalar(y);
98}
99
100static void LineSubDivide(const SkPoint a[2], double startT, double endT,
101 SkPoint sub[2]) {
102 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
103 _Line dst;
104 sub_divide(aLine, startT, endT, dst);
105 sub[0].fX = SkDoubleToScalar(dst[0].x);
106 sub[0].fY = SkDoubleToScalar(dst[0].y);
107 sub[1].fX = SkDoubleToScalar(dst[1].x);
108 sub[1].fY = SkDoubleToScalar(dst[1].y);
109}
110
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000111#if COMPARE_DOUBLE
112static void LineSubDivide(const SkPoint a[2], double startT, double endT,
113 _Line& dst) {
114 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
115 sub_divide(aLine, startT, endT, dst);
116}
117#endif
caryclark@google.com6680fb12012-02-07 22:10:51 +0000118
caryclark@google.comc6825902012-02-03 22:07:47 +0000119/*
120list of edges
121bounds for edge
122sort
123active T
124
125if a contour's bounds is outside of the active area, no need to create edges
126*/
127
128/* given one or more paths,
129 find the bounds of each contour, select the active contours
130 for each active contour, compute a set of edges
131 each edge corresponds to one or more lines and curves
132 leave edges unbroken as long as possible
133 when breaking edges, compute the t at the break but leave the control points alone
134
135 */
136
137void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray) {
138 SkPath::Iter iter(path, false);
139 SkPoint pts[4];
140 SkPath::Verb verb;
141 SkRect bounds;
142 bounds.setEmpty();
143 int count = 0;
144 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
145 switch (verb) {
146 case SkPath::kMove_Verb:
147 if (!bounds.isEmpty()) {
148 *boundsArray.append() = bounds;
149 }
150 bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
151 count = 0;
152 break;
153 case SkPath::kLine_Verb:
154 count = 1;
155 break;
156 case SkPath::kQuad_Verb:
157 count = 2;
158 break;
159 case SkPath::kCubic_Verb:
160 count = 3;
161 break;
162 case SkPath::kClose_Verb:
163 count = 0;
164 break;
165 default:
166 SkDEBUGFAIL("bad verb");
167 return;
168 }
169 for (int i = 1; i <= count; ++i) {
170 bounds.growToInclude(pts[i].fX, pts[i].fY);
171 }
172 }
173}
174
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000175static bool extendLine(const SkPoint line[2], const SkPoint& add) {
176 // FIXME: allow this to extend lines that have slopes that are nearly equal
177 SkScalar dx1 = line[1].fX - line[0].fX;
178 SkScalar dy1 = line[1].fY - line[0].fY;
179 SkScalar dx2 = add.fX - line[0].fX;
180 SkScalar dy2 = add.fY - line[0].fY;
181 return dx1 * dy2 == dx2 * dy1;
182}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000183
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000184// OPTIMIZATION: this should point to a list of input data rather than duplicating
185// the line data here. This would reduce the need to assemble the results.
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000186struct OutEdge {
187 bool operator<(const OutEdge& rh) const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000188 const SkPoint& first = fPts[0];
189 const SkPoint& rhFirst = rh.fPts[0];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000190 return first.fY == rhFirst.fY
191 ? first.fX < rhFirst.fX
192 : first.fY < rhFirst.fY;
193 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000194
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000195 SkPoint fPts[4];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000196 int fID; // id of edge generating data
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000197 uint8_t fVerb; // FIXME: not read from everywhere
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000198 bool fCloseCall; // edge is trimmable if not originally coincident
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000199};
200
caryclark@google.com6680fb12012-02-07 22:10:51 +0000201class OutEdgeBuilder {
202public:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000203 OutEdgeBuilder(bool fill)
204 : fFill(fill) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000205 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000206
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000207 void addLine(const SkPoint line[2], int id, bool closeCall) {
caryclark@google.comc17972e2012-02-20 21:33:22 +0000208 OutEdge& newEdge = fEdges.push_back();
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000209 newEdge.fPts[0] = line[0];
210 newEdge.fPts[1] = line[1];
211 newEdge.fVerb = SkPath::kLine_Verb;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000212 newEdge.fID = id;
213 newEdge.fCloseCall = closeCall;
214 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000215
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000216 bool trimLine(SkScalar y, int id) {
217 size_t count = fEdges.count();
218 while (count-- != 0) {
219 OutEdge& edge = fEdges[count];
220 if (edge.fID != id) {
221 continue;
222 }
223 if (edge.fCloseCall) {
224 return false;
225 }
226 SkASSERT(edge.fPts[0].fY <= y);
227 if (edge.fPts[1].fY <= y) {
228 continue;
229 }
230 edge.fPts[1].fX = edge.fPts[0].fX + (y - edge.fPts[0].fY)
231 * (edge.fPts[1].fX - edge.fPts[0].fX)
232 / (edge.fPts[1].fY - edge.fPts[0].fY);
233 edge.fPts[1].fY = y;
234 if (gShowDebugf) {
235 SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id,
236 edge.fPts[1].fX, y);
237 }
238 return true;
239 }
240 return false;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000241 }
242
243 void assemble(SkPath& simple) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000244 size_t listCount = fEdges.count();
245 if (listCount == 0) {
246 return;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000247 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000248 do {
249 size_t listIndex = 0;
250 int advance = 1;
251 while (listIndex < listCount && fTops[listIndex] == 0) {
252 ++listIndex;
253 }
254 if (listIndex >= listCount) {
255 break;
256 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000257 int closeEdgeIndex = -listIndex - 1;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000258 SkPoint firstPt, lastLine[2];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000259 bool doMove = true;
260 int edgeIndex;
261 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000262 SkPoint* ptArray = fEdges[listIndex].fPts;
263 uint8_t verb = fEdges[listIndex].fVerb;
264 SkPoint* start, * end;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000265 if (advance < 0) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000266 start = &ptArray[verb];
267 end = &ptArray[0];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000268 } else {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000269 start = &ptArray[0];
270 end = &ptArray[verb];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000271 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000272 switch (verb) {
273 case SkPath::kLine_Verb:
274 bool gap;
275 if (doMove) {
276 firstPt = *start;
277 simple.moveTo(start->fX, start->fY);
278 if (gShowDebugf) {
279 SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
280 start->fX, start->fY);
281 }
282 lastLine[0] = *start;
283 lastLine[1] = *end;
284 doMove = false;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000285 break;
286 }
287 gap = lastLine[1] != *start;
288 if (gap) {
caryclark@google.comd88e0892012-03-27 13:23:51 +0000289 // FIXME: see comment in bridge -- this probably
290 // conceals errors
291 SkASSERT(fFill && UlpsDiff(lastLine[1].fY, start->fY) <= 10);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000292 simple.lineTo(lastLine[1].fX, lastLine[1].fY);
293 if (gShowDebugf) {
294 SkDebugf("%s lineTo x (%g,%g)\n", __FUNCTION__,
295 lastLine[1].fX, lastLine[1].fY);
296 }
297 }
298 if (gap || !extendLine(lastLine, *end)) {
caryclark@google.comd88e0892012-03-27 13:23:51 +0000299 // FIXME: see comment in bridge -- this probably
300 // conceals errors
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000301 SkASSERT(lastLine[1] == *start ||
caryclark@google.comd88e0892012-03-27 13:23:51 +0000302 (fFill && UlpsDiff(lastLine[1].fY, start->fY) <= 10));
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000303 simple.lineTo(start->fX, start->fY);
304 if (gShowDebugf) {
305 SkDebugf("%s lineTo (%g,%g)\n", __FUNCTION__,
306 start->fX, start->fY);
307 }
308 lastLine[0] = *start;
309 }
310 lastLine[1] = *end;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000311 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000312 default:
313 // FIXME: add other curve types
314 ;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000315 }
316 if (advance < 0) {
317 edgeIndex = fTops[listIndex];
318 fTops[listIndex] = 0;
319 } else {
320 edgeIndex = fBottoms[listIndex];
321 fBottoms[listIndex] = 0;
322 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000323 if (edgeIndex) {
324 listIndex = abs(edgeIndex) - 1;
325 if (edgeIndex < 0) {
326 fTops[listIndex] = 0;
327 } else {
328 fBottoms[listIndex] = 0;
329 }
330 }
331 if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
332 if (lastLine[1] != firstPt) {
333 simple.lineTo(lastLine[1].fX, lastLine[1].fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000334 if (gShowDebugf) {
335 SkDebugf("%s lineTo last (%g, %g)\n", __FUNCTION__,
336 lastLine[1].fX, lastLine[1].fY);
337 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000338 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000339 simple.lineTo(firstPt.fX, firstPt.fY);
340 simple.close();
341 if (gShowDebugf) {
caryclark@google.com4917f172012-03-05 22:01:21 +0000342 SkDebugf("%s close (%g, %g)\n", __FUNCTION__,
343 firstPt.fX, firstPt.fY);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000344 }
345 break;
346 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000347 // if this and next edge go different directions
348 if (advance > 0 ^ edgeIndex < 0) {
349 advance = -advance;
350 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000351 } while (edgeIndex);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000352 } while (true);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000353 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000354
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000355 // sort points by y, then x
356 // if x/y is identical, sort bottoms before tops
357 // if identical and both tops/bottoms, sort by angle
358 static bool lessThan(SkTArray<OutEdge>& edges, const int one,
359 const int two) {
360 const OutEdge& oneEdge = edges[abs(one) - 1];
361 int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
362 const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
363 const OutEdge& twoEdge = edges[abs(two) - 1];
364 int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
365 const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
366 if (startPt1.fY != startPt2.fY) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000367 #if DEBUG_OUT_LESS_THAN
368 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
369 one, two, startPt1.fY, startPt2.fY,
370 startPt1.fY < startPt2.fY ? "true" : "false");
371 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000372 return startPt1.fY < startPt2.fY;
373 }
374 if (startPt1.fX != startPt2.fX) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000375 #if DEBUG_OUT_LESS_THAN
376 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
377 one, two, startPt1.fX, startPt2.fX,
378 startPt1.fX < startPt2.fX ? "true" : "false");
379 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000380 return startPt1.fX < startPt2.fX;
381 }
382 const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
383 const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
384 SkScalar dy1 = startPt1.fY - endPt1.fY;
385 SkScalar dy2 = startPt2.fY - endPt2.fY;
386 SkScalar dy1y2 = dy1 * dy2;
387 if (dy1y2 < 0) { // different signs
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000388 #if DEBUG_OUT_LESS_THAN
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000389 SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
390 dy1 > 0 ? "true" : "false");
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000391 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000392 return dy1 > 0; // one < two if one goes up and two goes down
393 }
394 if (dy1y2 == 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000395 #if DEBUG_OUT_LESS_THAN
396 SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
397 one, two, endPt1.fX < endPt2.fX ? "true" : "false");
398 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000399 return endPt1.fX < endPt2.fX;
400 }
401 SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
402 SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000403 #if DEBUG_OUT_LESS_THAN
404 SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
405 one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
406 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000407 return dy2 > 0 ^ dx1y2 < dx2y1;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000408 }
409
caryclark@google.com6008c6562012-02-15 22:01:16 +0000410 // Sort the indices of paired points and then create more indices so
411 // assemble() can find the next edge and connect the top or bottom
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000412 void bridge() {
413 size_t index;
414 size_t count = fEdges.count();
415 if (!count) {
416 return;
417 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000418 SkASSERT(!fFill || count > 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000419 fTops.setCount(count);
420 sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
421 fBottoms.setCount(count);
422 sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000423 SkTDArray<int> order;
424 for (index = 1; index <= count; ++index) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000425 *order.append() = -index;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000426 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000427 for (index = 1; index <= count; ++index) {
428 *order.append() = index;
429 }
430 QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000431 int* lastPtr = order.end() - 1;
432 int* leftPtr = order.begin();
433 while (leftPtr < lastPtr) {
434 int leftIndex = *leftPtr;
435 int leftOutIndex = abs(leftIndex) - 1;
436 const OutEdge& left = fEdges[leftOutIndex];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000437 int* rightPtr = leftPtr + 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000438 int rightIndex = *rightPtr;
439 int rightOutIndex = abs(rightIndex) - 1;
440 const OutEdge& right = fEdges[rightOutIndex];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000441 bool pairUp = fFill;
442 if (!pairUp) {
443 const SkPoint& leftMatch =
444 left.fPts[leftIndex < 0 ? 0 : left.fVerb];
445 const SkPoint& rightMatch =
446 right.fPts[rightIndex < 0 ? 0 : right.fVerb];
447 pairUp = leftMatch == rightMatch;
448 } else {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000449 #if DEBUG_OUT
caryclark@google.comd88e0892012-03-27 13:23:51 +0000450 // FIXME : not happy that error in low bit is allowed
451 // this probably conceals error elsewhere
452 if (UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
453 right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) > 1) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000454 *fMismatches.append() = leftIndex;
455 if (rightPtr == lastPtr) {
456 *fMismatches.append() = rightIndex;
457 }
458 pairUp = false;
459 }
460 #else
caryclark@google.comd88e0892012-03-27 13:23:51 +0000461 SkASSERT(UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
462 right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) <= 10);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000463 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000464 }
465 if (pairUp) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000466 if (leftIndex < 0) {
467 fTops[leftOutIndex] = rightIndex;
468 } else {
469 fBottoms[leftOutIndex] = rightIndex;
470 }
471 if (rightIndex < 0) {
472 fTops[rightOutIndex] = leftIndex;
473 } else {
474 fBottoms[rightOutIndex] = leftIndex;
475 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000476 ++rightPtr;
477 }
478 leftPtr = rightPtr;
479 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000480#if DEBUG_OUT
481 int* mismatch = fMismatches.begin();
482 while (mismatch != fMismatches.end()) {
483 int leftIndex = *mismatch++;
484 int leftOutIndex = abs(leftIndex) - 1;
485 const OutEdge& left = fEdges[leftOutIndex];
486 const SkPoint& leftPt = left.fPts[leftIndex < 0 ? 0 : left.fVerb];
487 SkDebugf("%s left=%d %s (%1.9g,%1.9g)\n",
488 __FUNCTION__, left.fID, leftIndex < 0 ? "top" : "bot",
489 leftPt.fX, leftPt.fY);
490 }
491 SkASSERT(fMismatches.count() == 0);
492#endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000493 }
494
caryclark@google.com6008c6562012-02-15 22:01:16 +0000495protected:
caryclark@google.com6680fb12012-02-07 22:10:51 +0000496 SkTArray<OutEdge> fEdges;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000497 SkTDArray<int> fTops;
498 SkTDArray<int> fBottoms;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000499 bool fFill;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000500#if DEBUG_OUT
501 SkTDArray<int> fMismatches;
502#endif
caryclark@google.com6680fb12012-02-07 22:10:51 +0000503};
504
caryclark@google.comc6825902012-02-03 22:07:47 +0000505// Bounds, unlike Rect, does not consider a vertical line to be empty.
506struct Bounds : public SkRect {
507 static bool Intersects(const Bounds& a, const Bounds& b) {
508 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
509 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
510 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000511
caryclark@google.com6008c6562012-02-15 22:01:16 +0000512 bool isEmpty() {
513 return fLeft > fRight || fTop > fBottom
514 || fLeft == fRight && fTop == fBottom
515 || isnan(fLeft) || isnan(fRight)
516 || isnan(fTop) || isnan(fBottom);
517 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000518};
519
caryclark@google.com4917f172012-03-05 22:01:21 +0000520class Intercepts {
521public:
522 Intercepts()
523 : fTopIntercepts(0)
524 , fBottomIntercepts(0) {
525 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000526
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000527#if DEBUG_DUMP
528 // FIXME: pass current verb as parameter
529 void dump(const SkPoint* pts) {
530 const char className[] = "Intercepts";
531 const int tab = 8;
532 for (int i = 0; i < fTs.count(); ++i) {
533 SkPoint out;
534 LineXYAtT(pts, fTs[i], &out);
caryclark@google.com752b60e2012-03-22 21:11:17 +0000535 SkDebugf("%*s.fTs[%d]=%1.9g (%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000536 className, i, fTs[i], out.fX, out.fY);
537 }
538 SkDebugf("%*s.fTopIntercepts=%d\n", tab + sizeof(className),
539 className, fTopIntercepts);
540 SkDebugf("%*s.fBottomIntercepts=%d\n", tab + sizeof(className),
541 className, fBottomIntercepts);
542 }
543#endif
544
caryclark@google.comc6825902012-02-03 22:07:47 +0000545 SkTDArray<double> fTs;
caryclark@google.com4917f172012-03-05 22:01:21 +0000546 int fTopIntercepts;
547 int fBottomIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000548};
549
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000550struct HorizontalEdge {
551 bool operator<(const HorizontalEdge& rh) const {
552 return fY == rh.fY ? fLeft == rh.fLeft ? fRight < rh.fRight
553 : fLeft < rh.fLeft : fY < rh.fY;
554 }
555
556#if DEBUG_DUMP
557 void dump() {
558 const char className[] = "HorizontalEdge";
559 const int tab = 4;
caryclark@google.com752b60e2012-03-22 21:11:17 +0000560 SkDebugf("%*s.fLeft=%1.9g\n", tab + sizeof(className), className, fLeft);
561 SkDebugf("%*s.fRight=%1.9g\n", tab + sizeof(className), className, fRight);
562 SkDebugf("%*s.fY=%1.9g\n", tab + sizeof(className), className, fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000563 }
564#endif
565
566 SkScalar fLeft;
567 SkScalar fRight;
568 SkScalar fY;
569};
570
caryclark@google.com6680fb12012-02-07 22:10:51 +0000571struct InEdge {
572 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000573 return fBounds.fTop == rh.fBounds.fTop
574 ? fBounds.fLeft < rh.fBounds.fLeft
575 : fBounds.fTop < rh.fBounds.fTop;
576 }
577
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000578 // Avoid collapsing t values that are close to the same since
579 // we walk ts to describe consecutive intersections. Since a pair of ts can
580 // be nearly equal, any problems caused by this should be taken care
581 // of later.
582 int add(double* ts, size_t count, ptrdiff_t verbIndex) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000583 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
caryclark@google.com6008c6562012-02-15 22:01:16 +0000584 bool foundIntercept = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000585 int insertedAt = -1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000586 Intercepts& intercepts = fIntercepts[verbIndex];
caryclark@google.comc6825902012-02-03 22:07:47 +0000587 for (size_t index = 0; index < count; ++index) {
588 double t = ts[index];
caryclark@google.com4917f172012-03-05 22:01:21 +0000589 if (t <= 0) {
590 fContainsIntercepts |= ++intercepts.fTopIntercepts > 1;
591 continue;
592 }
593 if (t >= 1) {
594 fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000595 continue;
596 }
597 foundIntercept = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000598 size_t tCount = intercepts.fTs.count();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000599 double delta;
caryclark@google.comc6825902012-02-03 22:07:47 +0000600 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
601 if (t <= intercepts.fTs[idx2]) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000602 // FIXME: ? if (t < intercepts.fTs[idx2]) // failed
603 delta = intercepts.fTs[idx2] - t;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000604 if (delta > 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000605 insertedAt = idx2;
caryclark@google.comc6825902012-02-03 22:07:47 +0000606 *intercepts.fTs.insert(idx2) = t;
caryclark@google.comc6825902012-02-03 22:07:47 +0000607 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000608 goto nextPt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000609 }
610 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000611 if (tCount == 0 || (delta = t - intercepts.fTs[tCount - 1]) > 0) {
612 insertedAt = tCount;
caryclark@google.comc6825902012-02-03 22:07:47 +0000613 *intercepts.fTs.append() = t;
614 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000615 nextPt:
616 ;
caryclark@google.comc6825902012-02-03 22:07:47 +0000617 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000618 fContainsIntercepts |= foundIntercept;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000619 return insertedAt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000620 }
621
caryclark@google.com6680fb12012-02-07 22:10:51 +0000622 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000623 // FIXME: in the pathological case where there is a ton of edges, binary search?
624 size_t count = fCached.count();
625 for (size_t index = 0; index < count; ++index) {
626 if (edge == fCached[index]) {
627 return true;
628 }
629 if (edge < fCached[index]) {
630 *fCached.insert(index) = edge;
631 return false;
632 }
633 }
634 *fCached.append() = edge;
635 return false;
636 }
637
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000638 void complete(signed char winding, int id) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000639 SkPoint* ptPtr = fPts.begin();
640 SkPoint* ptLast = fPts.end();
641 if (ptPtr == ptLast) {
642 SkDebugf("empty edge\n");
643 SkASSERT(0);
644 // FIXME: delete empty edge?
645 return;
646 }
647 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
648 ++ptPtr;
649 while (ptPtr != ptLast) {
650 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
651 ++ptPtr;
652 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000653 fIntercepts.push_back_n(fVerbs.count());
caryclark@google.com6680fb12012-02-07 22:10:51 +0000654 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
655 size_t index;
656 size_t last = fPts.count() - 1;
657 for (index = 0; index < last; ++index, --last) {
658 SkTSwap<SkPoint>(fPts[index], fPts[last]);
659 }
660 last = fVerbs.count() - 1;
661 for (index = 0; index < last; ++index, --last) {
662 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
663 }
664 }
665 fContainsIntercepts = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000666 fID = id;
caryclark@google.comc6825902012-02-03 22:07:47 +0000667 }
668
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000669#if DEBUG_DUMP
670 void dump() {
671 int i;
672 const char className[] = "InEdge";
673 const int tab = 4;
674 SkDebugf("InEdge %p (edge=%d)\n", this, fID);
675 for (i = 0; i < fCached.count(); ++i) {
676 SkDebugf("%*s.fCached[%d]=0x%08x\n", tab + sizeof(className),
677 className, i, fCached[i]);
678 }
679 uint8_t* verbs = fVerbs.begin();
680 SkPoint* pts = fPts.begin();
681 for (i = 0; i < fIntercepts.count(); ++i) {
682 SkDebugf("%*s.fIntercepts[%d]:\n", tab + sizeof(className),
683 className, i);
684 // FIXME: take current verb into consideration
685 fIntercepts[i].dump(pts);
686 pts += *verbs++;
687 }
688 for (i = 0; i < fPts.count(); ++i) {
caryclark@google.com752b60e2012-03-22 21:11:17 +0000689 SkDebugf("%*s.fPts[%d]=(%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000690 className, i, fPts[i].fX, fPts[i].fY);
691 }
692 for (i = 0; i < fVerbs.count(); ++i) {
693 SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className),
694 className, i, fVerbs[i]);
695 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000696 SkDebugf("%*s.fBounds=(%1.9g. %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000697 className, fBounds.fLeft, fBounds.fTop,
698 fBounds.fRight, fBounds.fBottom);
699 SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className,
700 fWinding);
701 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
702 className, fContainsIntercepts);
703 }
704#endif
705
caryclark@google.comc6825902012-02-03 22:07:47 +0000706 // temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +0000707 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +0000708 SkTArray<Intercepts> fIntercepts; // one per verb
caryclark@google.com4917f172012-03-05 22:01:21 +0000709
caryclark@google.comc6825902012-02-03 22:07:47 +0000710 // persistent data
711 SkTDArray<SkPoint> fPts;
712 SkTDArray<uint8_t> fVerbs;
713 Bounds fBounds;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000714 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +0000715 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000716 bool fContainsIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000717};
718
caryclark@google.com6680fb12012-02-07 22:10:51 +0000719class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +0000720public:
721
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000722InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges,
723 SkTDArray<HorizontalEdge>& horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000724 : fPath(path)
725 , fCurrentEdge(NULL)
726 , fEdges(edges)
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000727 , fID(0)
728 , fHorizontalEdges(horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000729 , fIgnoreHorizontal(ignoreHorizontal)
730{
731 walk();
732}
733
734protected:
735
736void addEdge() {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000737 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +0000738 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
739 fPtOffset = 1;
740 *fCurrentEdge->fVerbs.append() = fVerb;
741}
742
caryclark@google.com6008c6562012-02-15 22:01:16 +0000743bool complete() {
744 if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000745 fCurrentEdge->complete(fWinding, ++fID);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000746 fCurrentEdge = NULL;
747 return true;
748 }
749 return false;
750}
751
caryclark@google.comc6825902012-02-03 22:07:47 +0000752int direction(int count) {
753 fPtCount = count;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000754 if (fIgnoreHorizontal && isHorizontal()) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000755 return 0;
756 }
757 int last = count - 1;
758 return fPts[0].fY == fPts[last].fY
caryclark@google.com6680fb12012-02-07 22:10:51 +0000759 ? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX
760 ? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +0000761}
762
763bool isHorizontal() {
764 SkScalar y = fPts[0].fY;
765 for (int i = 1; i < fPtCount; ++i) {
766 if (fPts[i].fY != y) {
767 return false;
768 }
769 }
770 return true;
771}
772
773void startEdge() {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000774 if (!fCurrentEdge) {
775 fCurrentEdge = fEdges.push_back_n(1);
776 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000777 fWinding = 0;
778 fPtOffset = 0;
779}
780
781void walk() {
782 SkPath::Iter iter(fPath, true);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000783 int winding = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +0000784 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
785 switch (fVerb) {
786 case SkPath::kMove_Verb:
caryclark@google.comc6825902012-02-03 22:07:47 +0000787 startEdge();
788 continue;
789 case SkPath::kLine_Verb:
790 winding = direction(2);
791 break;
792 case SkPath::kQuad_Verb:
793 winding = direction(3);
794 break;
795 case SkPath::kCubic_Verb:
796 winding = direction(4);
797 break;
798 case SkPath::kClose_Verb:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000799 SkASSERT(fCurrentEdge);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000800 complete();
caryclark@google.comc6825902012-02-03 22:07:47 +0000801 continue;
802 default:
803 SkDEBUGFAIL("bad verb");
804 return;
805 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000806 if (winding == 0) {
807 HorizontalEdge* horizontalEdge = fHorizontalEdges.append();
808 // FIXME: for degenerate quads and cubics, compute x extremes
809 horizontalEdge->fLeft = fPts[0].fX;
810 horizontalEdge->fRight = fPts[fVerb].fX;
811 horizontalEdge->fY = fPts[0].fY;
812 if (horizontalEdge->fLeft > horizontalEdge->fRight) {
813 SkTSwap<SkScalar>(horizontalEdge->fLeft, horizontalEdge->fRight);
814 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000815 if (complete()) {
816 startEdge();
817 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000818 continue;
819 }
820 if (fWinding + winding == 0) {
821 // FIXME: if prior verb or this verb is a horizontal line, reverse
822 // it instead of starting a new edge
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000823 SkASSERT(fCurrentEdge);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000824 if (complete()) {
825 startEdge();
826 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000827 }
828 fWinding = winding;
829 addEdge();
830 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +0000831 if (!complete()) {
832 if (fCurrentEdge) {
833 fEdges.pop_back();
834 }
835 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000836}
837
838private:
839 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000840 InEdge* fCurrentEdge;
841 SkTArray<InEdge>& fEdges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000842 SkTDArray<HorizontalEdge>& fHorizontalEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +0000843 SkPoint fPts[4];
844 SkPath::Verb fVerb;
845 int fPtCount;
846 int fPtOffset;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000847 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +0000848 int8_t fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000849 bool fIgnoreHorizontal;
850};
851
caryclark@google.com6680fb12012-02-07 22:10:51 +0000852struct WorkEdge {
853 SkScalar bottom() const {
854 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +0000855 }
856
caryclark@google.com6680fb12012-02-07 22:10:51 +0000857 void init(const InEdge* edge) {
858 fEdge = edge;
859 fPts = edge->fPts.begin();
860 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +0000861 }
862
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000863 bool advance() {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000864 SkASSERT(fVerb < fEdge->fVerbs.end());
865 fPts += *fVerb++;
866 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +0000867 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000868
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000869 SkPath::Verb lastVerb() const {
870 SkASSERT(fVerb > fEdge->fVerbs.begin());
871 return (SkPath::Verb) fVerb[-1];
872 }
873
caryclark@google.comc6825902012-02-03 22:07:47 +0000874
875 SkPath::Verb verb() const {
876 return (SkPath::Verb) *fVerb;
877 }
878
caryclark@google.com6008c6562012-02-15 22:01:16 +0000879 ptrdiff_t verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000880 return fVerb - fEdge->fVerbs.begin();
881 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000882
caryclark@google.com6680fb12012-02-07 22:10:51 +0000883 int winding() const {
884 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000885 }
886
caryclark@google.com6680fb12012-02-07 22:10:51 +0000887 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +0000888 const SkPoint* fPts;
889 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +0000890};
891
caryclark@google.com6680fb12012-02-07 22:10:51 +0000892// always constructed with SkTDArray because new edges are inserted
893// this may be a inappropriate optimization, suggesting that a separate array of
894// ActiveEdge* may be faster to insert and search
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000895
896// OPTIMIZATION: Brian suggests that global sorting should be unnecessary, since
897// as active edges are introduced, only local sorting should be required
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000898class ActiveEdge {
899public:
caryclark@google.com6008c6562012-02-15 22:01:16 +0000900 bool operator<(const ActiveEdge& rh) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +0000901 double topD = fAbove.fX - rh.fAbove.fX;
902 if (rh.fAbove.fY < fAbove.fY) {
903 topD = (rh.fBelow.fY - rh.fAbove.fY) * topD
904 - (fAbove.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
905 } else if (rh.fAbove.fY > fAbove.fY) {
906 topD = (fBelow.fY - fAbove.fY) * topD
907 + (rh.fAbove.fY - fAbove.fY) * (fBelow.fX - fAbove.fX);
908 }
909 double botD = fBelow.fX - rh.fBelow.fX;
910 if (rh.fBelow.fY > fBelow.fY) {
911 botD = (rh.fBelow.fY - rh.fAbove.fY) * botD
912 - (fBelow.fY - rh.fBelow.fY) * (rh.fBelow.fX - rh.fAbove.fX);
913 } else if (rh.fBelow.fY < fBelow.fY) {
914 botD = (fBelow.fY - fAbove.fY) * botD
915 + (rh.fBelow.fY - fBelow.fY) * (fBelow.fX - fAbove.fX);
916 }
917 // return sign of greater absolute value
918 return (fabs(topD) > fabs(botD) ? topD : botD) < 0;
919 }
920
921 // OPTIMIZATION: fold return statements into one
922 bool operator_less_than(const ActiveEdge& rh) const {
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000923 if (rh.fAbove.fY - fAbove.fY > fBelow.fY - rh.fAbove.fY
924 && fBelow.fY < rh.fBelow.fY
925 || fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - fAbove.fY
926 && rh.fBelow.fY < fBelow.fY) {
927 // FIXME: need to compute distance, not check for points equal
928 const SkPoint& check = rh.fBelow.fY <= fBelow.fY
929 && fBelow != rh.fBelow ? rh.fBelow :
930 rh.fAbove;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000931 #if DEBUG_ACTIVE_LESS_THAN
932 SkDebugf("%s 1 %c %cthis (edge=%d) {%g,%g %g,%g}"
933 " < rh (edge=%d) {%g,%g %g,%g} upls=(%d)\n", __FUNCTION__,
934 rh.fBelow.fY <= fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
935 (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
936 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX) ? ' '
937 : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
938 rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
939 UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
940 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX)));
941 #endif
caryclark@google.com752b60e2012-03-22 21:11:17 +0000942 #if COMPARE_DOUBLE
943 SkASSERT(((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
944 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX))
945 == ((check.fY - fDAbove.y) * (fDBelow.x - fDAbove.x)
946 < (fDBelow.y - fDAbove.y) * (check.fX - fDAbove.x)));
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000947 #endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000948 return (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
949 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX);
950 }
951 // FIXME: need to compute distance, not check for points equal
952 const SkPoint& check = fBelow.fY <= rh.fBelow.fY
953 && fBelow != rh.fBelow ? fBelow : fAbove;
caryclark@google.com752b60e2012-03-22 21:11:17 +0000954 #if DEBUG_ACTIVE_LESS_THAN
955 SkDebugf("%s 2 %c %cthis (edge=%d) {%g,%g %g,%g}"
956 " < rh (edge=%d) {%g,%g %g,%g} upls=(%d) (%d,%d)\n", __FUNCTION__,
957 fBelow.fY <= rh.fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
958 (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
959 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)
960 ? ' ' : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
961 rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
962 UlpsDiff((rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX),
963 (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)),
964 UlpsDiff(fBelow.fX, rh.fBelow.fX), UlpsDiff(fBelow.fY, rh.fBelow.fY));
965 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000966 #if COMPARE_DOUBLE
967 SkASSERT(((rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
968 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX))
969 == ((rh.fDBelow.y - rh.fDAbove.y) * (check.fX - rh.fDAbove.x)
970 < (check.fY - rh.fDAbove.y) * (rh.fDBelow.x - rh.fDAbove.x)));
971 #endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000972 return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
973 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000974 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000975
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000976 // If a pair of edges are nearly coincident for some span, add a T in the
977 // edge so it can be shortened to match the other edge. Note that another
978 // approach is to trim the edge after it is added to the OutBuilder list --
979 // FIXME: since this has no effect if the edge is already done (i.e.,
980 // fYBottom >= y) maybe this can only be done by calling trimLine later.
981 void addTatYBelow(SkScalar y) {
982 if (fBelow.fY <= y || fYBottom >= y) {
983 return;
984 }
985 addTatYInner(y);
986 fFixBelow = true;
987 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000988
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000989 void addTatYAbove(SkScalar y) {
990 if (fBelow.fY <= y) {
991 return;
992 }
993 addTatYInner(y);
994 }
995
996 void addTatYInner(SkScalar y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +0000997 if (fWorkEdge.fPts[0].fY > y) {
998 backup(y);
999 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001000 SkScalar left = fWorkEdge.fPts[0].fX;
1001 SkScalar right = fWorkEdge.fPts[1].fX;
1002 if (left > right) {
1003 SkTSwap(left, right);
1004 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001005 double ts[2];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001006 int pts = LineIntersect(fWorkEdge.fPts, left, right, y, ts);
1007 SkASSERT(pts == 1);
1008 // An ActiveEdge or WorkEdge has no need to modify the T values computed
1009 // in the InEdge, except in the following case. If a pair of edges are
1010 // nearly coincident, this may not be detected when the edges are
1011 // intersected. Later, when sorted, and this near-coincidence is found,
1012 // an additional t value must be added, requiring the cast below.
1013 InEdge* writable = const_cast<InEdge*>(fWorkEdge.fEdge);
1014 int insertedAt = writable->add(ts, pts, fWorkEdge.verbIndex());
caryclark@google.com752b60e2012-03-22 21:11:17 +00001015 #if DEBUG_ADJUST_COINCIDENT
1016 SkDebugf("%s edge=%d y=%1.9g t=%1.9g\n", __FUNCTION__, ID(), y, ts[0]);
1017 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001018 if (insertedAt >= 0) {
1019 if (insertedAt + 1 < fTIndex) {
1020 SkASSERT(insertedAt + 2 == fTIndex);
1021 --fTIndex;
1022 }
1023 }
1024 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001025
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001026 bool advanceT() {
1027 SkASSERT(fTIndex <= fTs->count());
1028 return ++fTIndex <= fTs->count();
1029 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001030
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001031 bool advance() {
1032 // FIXME: flip sense of next
1033 bool result = fWorkEdge.advance();
1034 fDone = !result;
1035 initT();
1036 return result;
1037 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001038
1039 void backup(SkScalar y) {
1040 do {
1041 SkASSERT(fWorkEdge.fEdge->fVerbs.begin() < fWorkEdge.fVerb);
1042 fWorkEdge.fPts -= *--fWorkEdge.fVerb;
1043 SkASSERT(fWorkEdge.fEdge->fPts.begin() <= fWorkEdge.fPts);
1044 } while (fWorkEdge.fPts[0].fY >= y);
1045 initT();
1046 fTIndex = fTs->count() + 1;
1047 }
1048
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001049 void calcLeft(SkScalar y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001050 // OPTIMIZE: put a kDone_Verb at the end of the verb list?
caryclark@google.com4917f172012-03-05 22:01:21 +00001051 if (fDone || fBelow.fY > y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001052 return; // nothing to do; use last
caryclark@google.com4917f172012-03-05 22:01:21 +00001053 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001054 calcLeft();
caryclark@google.com752b60e2012-03-22 21:11:17 +00001055 if (fAbove.fY == fBelow.fY) {
1056 SkDebugf("%s edge=%d fAbove.fY != fBelow.fY %1.9g\n", __FUNCTION__,
1057 ID(), fAbove.fY);
1058 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001059 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001060
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001061 void calcLeft() {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001062 switch (fWorkEdge.verb()) {
1063 case SkPath::kLine_Verb: {
1064 // OPTIMIZATION: if fXAbove, fXBelow have already been computed
1065 // for the fTIndex, don't do it again
1066 // For identical x, this lets us know which edge is first.
1067 // If both edges have T values < 1, check x at next T (fXBelow).
1068 int add = (fTIndex <= fTs->count()) - 1;
1069 double tAbove = t(fTIndex + add);
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001070 // OPTIMIZATION: may not need Y
1071 LineXYAtT(fWorkEdge.fPts, tAbove, &fAbove);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001072 double tBelow = t(fTIndex - ~add);
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001073 LineXYAtT(fWorkEdge.fPts, tBelow, &fBelow);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001074 SkASSERT(tAbove != tBelow);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001075 while (fAbove.fY == fBelow.fY) {
1076 if (add < 0) {
1077 add -= 1;
1078 SkASSERT(fTIndex + add >= 0);
1079 tAbove = t(fTIndex + add);
1080 LineXYAtT(fWorkEdge.fPts, tAbove, &fAbove);
1081 } else {
1082 add += 1;
1083 SkASSERT(fTIndex - ~add <= fTs->count() + 1);
1084 tBelow = t(fTIndex - ~add);
1085 LineXYAtT(fWorkEdge.fPts, tBelow, &fBelow);
1086 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001087 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001088 #if COMPARE_DOUBLE
1089 LineXYAtT(fWorkEdge.fPts, tAbove, &fDAbove);
1090 LineXYAtT(fWorkEdge.fPts, tBelow, &fDBelow);
1091 #endif
1092 #if DEBUG_ABOVE_BELOW
1093 fTAbove = tAbove;
1094 fTBelow = tBelow;
1095 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001096 break;
1097 }
1098 default:
1099 // FIXME: add support for all curve types
1100 SkASSERT(0);
1101 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001102 }
1103
caryclark@google.com752b60e2012-03-22 21:11:17 +00001104 bool done(SkScalar bottom) const {
1105 return fDone || fYBottom >= bottom;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001106 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001107
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001108 void fixBelow() {
1109 if (fFixBelow) {
1110 LineXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1111 fFixBelow = false;
1112 }
1113 }
1114
caryclark@google.com6680fb12012-02-07 22:10:51 +00001115 void init(const InEdge* edge) {
1116 fWorkEdge.init(edge);
1117 initT();
caryclark@google.com4917f172012-03-05 22:01:21 +00001118 fBelow.fY = SK_ScalarMin;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001119 fDone = false;
1120 fYBottom = SK_ScalarMin;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001121 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001122
caryclark@google.com6680fb12012-02-07 22:10:51 +00001123 void initT() {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001124 const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
1125 SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
1126 const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
1127 fTs = &interceptPtr->fTs;
1128 // the above is conceptually the same as
1129 // fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
1130 // but templated arrays don't allow returning a pointer to the end() element
caryclark@google.com6680fb12012-02-07 22:10:51 +00001131 fTIndex = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +00001132 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001133
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001134 // OPTIMIZATION: record if two edges are coincident when the are intersected
1135 // It's unclear how to do this -- seems more complicated than recording the
1136 // t values, since the same t values could exist intersecting non-coincident
1137 // edges.
1138 bool isCoincidentWith(const ActiveEdge* edge, SkScalar y) const {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001139
caryclark@google.comd88e0892012-03-27 13:23:51 +00001140#if 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001141 if (!fAbove.equalsWithinTolerance(edge->fAbove, MIN_PT_DELTA)
1142 || !fBelow.equalsWithinTolerance(edge->fBelow, MIN_PT_DELTA)) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001143 return false;
1144 }
caryclark@google.comd88e0892012-03-27 13:23:51 +00001145#else
1146 if (fAbove != edge->fAbove || fBelow != edge->fBelow) {
1147 return false;
1148 }
1149#endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001150 uint8_t verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
1151 uint8_t edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb()
1152 : edge->fWorkEdge.verb();
1153 if (verb != edgeVerb) {
1154 return false;
1155 }
1156 switch (verb) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001157 case SkPath::kLine_Verb: {
caryclark@google.com4917f172012-03-05 22:01:21 +00001158 return true;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001159 }
1160 default:
1161 // FIXME: add support for all curve types
1162 SkASSERT(0);
1163 }
1164 return false;
1165 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001166
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001167 // The shortest close call edge should be moved into a position where
1168 // it contributes if the winding is transitioning to or from zero.
1169 bool swapClose(const ActiveEdge* next, int prev, int wind, int mask) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001170#if DEBUG_ADJUST_COINCIDENT
1171 SkDebugf("%s edge=%d (%g) next=%d (%g) prev=%d wind=%d nextWind=%d\n",
1172 __FUNCTION__, ID(), fBelow.fY, next->ID(), next->fBelow.fY,
1173 prev, wind, wind + next->fWorkEdge.winding());
1174#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001175 if ((prev & mask) == 0 || (wind & mask) == 0) {
1176 return next->fBelow.fY < fBelow.fY;
1177 }
1178 int nextWinding = wind + next->fWorkEdge.winding();
1179 if ((nextWinding & mask) == 0) {
1180 return fBelow.fY < next->fBelow.fY;
1181 }
1182 return false;
1183 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001184
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001185 bool swapCoincident(const ActiveEdge* edge, SkScalar bottom) const {
1186 if (fBelow.fY >= bottom || fDone || edge->fDone) {
1187 return false;
1188 }
1189 ActiveEdge thisWork = *this;
1190 ActiveEdge edgeWork = *edge;
1191 while ((thisWork.advanceT() || thisWork.advance())
1192 && (edgeWork.advanceT() || edgeWork.advance())) {
1193 thisWork.calcLeft();
1194 edgeWork.calcLeft();
1195 if (thisWork < edgeWork) {
1196 return false;
1197 }
1198 if (edgeWork < thisWork) {
1199 return true;
1200 }
1201 }
1202 return false;
1203 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001204
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001205 bool tooCloseToCall(const ActiveEdge* edge) const {
1206 int ulps;
caryclark@google.comd88e0892012-03-27 13:23:51 +00001207 // FIXME: the first variation works better (or at least causes fewer tests
1208 // to fail than the second, although the second's logic better matches the
1209 // current sort criteria. Need to track down the cause of the crash, and
1210 // see if the second case isn't somehow buggy.
1211#if 01
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001212 // FIXME: don't compare points for equality
1213 // OPTIMIZATION: refactor to make one call to UlpsDiff
1214 if (edge->fAbove.fY - fAbove.fY > fBelow.fY - edge->fAbove.fY
1215 && fBelow.fY < edge->fBelow.fY
1216 || fAbove.fY - edge->fAbove.fY < edge->fBelow.fY - fAbove.fY
1217 && edge->fBelow.fY < fBelow.fY) {
1218 const SkPoint& check = edge->fBelow.fY <= fBelow.fY
1219 && fBelow != edge->fBelow ? edge->fBelow :
1220 edge->fAbove;
1221 ulps = UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
1222 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX));
1223 } else {
1224 const SkPoint& check = fBelow.fY <= edge->fBelow.fY
1225 && fBelow != edge->fBelow ? fBelow : fAbove;
1226 ulps = UlpsDiff((edge->fBelow.fY - edge->fAbove.fY)
1227 * (check.fX - edge->fAbove.fX),
1228 (check.fY - edge->fAbove.fY)
1229 * (edge->fBelow.fX - edge->fAbove.fX));
1230 }
caryclark@google.comd88e0892012-03-27 13:23:51 +00001231#else
1232 double t1, t2, b1, b2;
1233 if (edge->fAbove.fY < fAbove.fY) {
1234 t1 = (edge->fBelow.fY - edge->fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
1235 t2 = (fAbove.fY - edge->fAbove.fY) * (edge->fBelow.fX - edge->fAbove.fX);
1236 } else if (edge->fAbove.fY > fAbove.fY) {
1237 t1 = (fBelow.fY - fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
1238 t2 = (fAbove.fY - edge->fAbove.fY) * (fBelow.fX - fAbove.fX);
1239 } else {
1240 t1 = fAbove.fX;
1241 t2 = edge->fAbove.fX;
1242 }
1243 if (edge->fBelow.fY > fBelow.fY) {
1244 b1 = (edge->fBelow.fY - edge->fAbove.fY) * (fBelow.fX - edge->fBelow.fX);
1245 b2 = (fBelow.fY - edge->fBelow.fY) * (edge->fBelow.fX - edge->fAbove.fX);
1246 } else if (edge->fBelow.fY < fBelow.fY) {
1247 b1 = (fBelow.fY - fAbove.fY) * (fBelow.fX - edge->fBelow.fX);
1248 b2 = (fBelow.fY - edge->fBelow.fY) * (fBelow.fX - fAbove.fX);
1249 } else {
1250 b1 = fBelow.fX;
1251 b2 = edge->fBelow.fX;
1252 }
1253 if (fabs(t1 - t2) > fabs(b1 - b2)) {
1254 ulps = UlpsDiff(t1, t2);
1255 } else {
1256 ulps = UlpsDiff(b1, b2);
1257 }
1258#if DEBUG_ADJUST_COINCIDENT
1259 SkDebugf("%s this=%d edge=%d ulps=%d\n", __FUNCTION__, ID(), edge->ID(),
1260 ulps);
1261#endif
1262#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001263 return ulps >= 0 && ulps <= 32;
1264 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001265
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001266 double nextT() const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001267 SkASSERT(fTIndex <= fTs->count());
1268 return t(fTIndex + 1);
1269 }
caryclark@google.com6680fb12012-02-07 22:10:51 +00001270
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001271 double t() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001272 if (fTIndex == 0) {
1273 return 0;
1274 }
1275 if (fTIndex > fTs->count()) {
1276 return 1;
1277 }
1278 return (*fTs)[fTIndex - 1];
1279 }
1280
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001281 double t(int tIndex) const {
1282 if (tIndex == 0) {
1283 return 0;
1284 }
1285 if (tIndex > fTs->count()) {
1286 return 1;
1287 }
1288 return (*fTs)[tIndex - 1];
1289 }
1290
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001291 // FIXME: debugging only
caryclark@google.com752b60e2012-03-22 21:11:17 +00001292 int ID() const {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001293 return fWorkEdge.fEdge->fID;
1294 }
1295
1296public:
caryclark@google.com6680fb12012-02-07 22:10:51 +00001297 WorkEdge fWorkEdge;
1298 const SkTDArray<double>* fTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001299 SkPoint fAbove;
1300 SkPoint fBelow;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001301#if COMPARE_DOUBLE
1302 _Point fDAbove;
1303 _Point fDBelow;
1304#endif
1305#if DEBUG_ABOVE_BELOW
1306 double fTAbove;
1307 double fTBelow;
1308#endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001309 SkScalar fYBottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001310 int fCoincident;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001311 int fTIndex;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001312 bool fSkip; // OPTIMIZATION: use bitfields?
1313 bool fCloseCall;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001314 bool fDone;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001315 bool fFixBelow;
caryclark@google.comc6825902012-02-03 22:07:47 +00001316};
1317
caryclark@google.com6680fb12012-02-07 22:10:51 +00001318static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001319 size_t count = activeEdges.count();
1320 for (size_t index = 0; index < count; ++index) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001321 if (edge == activeEdges[index].fWorkEdge.fEdge) {
1322 return;
1323 }
1324 }
1325 ActiveEdge* active = activeEdges.append();
1326 active->init(edge);
1327}
1328
caryclark@google.com4917f172012-03-05 22:01:21 +00001329// Find any intersections in the range of active edges. A pair of edges, on
1330// either side of another edge, may change the winding contribution for part of
1331// the edge.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001332// Keep horizontal edges just for
caryclark@google.com4917f172012-03-05 22:01:21 +00001333// the purpose of computing when edges change their winding contribution, since
1334// this is essentially computing the horizontal intersection.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001335static void addBottomT(InEdge** currentPtr, InEdge** lastPtr,
1336 HorizontalEdge** horizontal) {
1337 InEdge** testPtr = currentPtr - 1;
1338 HorizontalEdge* horzEdge = *horizontal;
1339 SkScalar left = horzEdge->fLeft;
1340 SkScalar bottom = horzEdge->fY;
1341 while (++testPtr != lastPtr) {
1342 InEdge* test = *testPtr;
1343 if (test->fBounds.fBottom <= bottom || test->fBounds.fRight <= left) {
1344 continue;
1345 }
1346 WorkEdge wt;
1347 wt.init(test);
1348 do {
1349 HorizontalEdge** sorted = horizontal;
1350 horzEdge = *sorted;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001351 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001352 if (wt.verb() == SkPath::kLine_Verb) {
1353 double wtTs[2];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001354 int pts = LineIntersect(wt.fPts, horzEdge->fLeft,
1355 horzEdge->fRight, horzEdge->fY, wtTs);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001356 if (pts) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001357#if DEBUG_ADD_BOTTOM_TS
1358 SkDebugf("%s y=%g wtTs[0]=%g (%g,%g, %g,%g)\n", __FUNCTION__,
1359 horzEdge->fY, wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
1360 wt.fPts[1].fX, wt.fPts[1].fY);
1361 if (pts == 2) {
1362 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1363 }
1364#endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001365 test->add(wtTs, pts, wt.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001366 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001367 } else {
1368 // FIXME: add all curve types
1369 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001370 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001371 horzEdge = *++sorted;
1372 } while (horzEdge->fY == bottom
1373 && horzEdge->fLeft <= test->fBounds.fRight);
1374 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001375 }
1376}
1377
1378static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001379 InEdge** testPtr = currentPtr - 1;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001380 // FIXME: lastPtr should be past the point of interest, so
1381 // test below should be lastPtr - 2
1382 // that breaks testSimplifyTriangle22, so further investigation is needed
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001383 while (++testPtr != lastPtr - 1) {
1384 InEdge* test = *testPtr;
1385 InEdge** nextPtr = testPtr;
1386 do {
1387 InEdge* next = *++nextPtr;
1388 if (test->cached(next)) {
1389 continue;
1390 }
1391 if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
1392 continue;
1393 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001394 WorkEdge wt, wn;
1395 wt.init(test);
1396 wn.init(next);
1397 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001398 if (wt.verb() == SkPath::kLine_Verb
1399 && wn.verb() == SkPath::kLine_Verb) {
1400 double wtTs[2], wnTs[2];
1401 int pts = LineIntersect(wt.fPts, wn.fPts, wtTs, wnTs);
1402 if (pts) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001403#if DEBUG_ADD_INTERSECTING_TS
1404 SkPoint wtOutPt, wnOutPt;
1405 LineXYAtT(wt.fPts, wtTs[0], &wtOutPt);
1406 LineXYAtT(wn.fPts, wnTs[0], &wnOutPt);
1407 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
1408 __FUNCTION__,
1409 wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
1410 wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY,
1411 test->fID, next->fID);
1412 if (pts == 2) {
1413 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1414 }
1415 SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
1416 __FUNCTION__,
1417 wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY,
1418 wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY,
1419 test->fID, next->fID);
1420 if (pts == 2) {
1421 SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
1422 }
1423#endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001424 test->add(wtTs, pts, wt.verbIndex());
1425 next->add(wnTs, pts, wn.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001426 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001427 } else {
1428 // FIXME: add all combinations of curve types
1429 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001430 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001431 } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
1432 } while (nextPtr != lastPtr);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001433 }
1434}
1435
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001436static InEdge** advanceEdges(SkTDArray<ActiveEdge>* activeEdges,
caryclark@google.com6008c6562012-02-15 22:01:16 +00001437 InEdge** currentPtr, InEdge** lastPtr, SkScalar y) {
1438 InEdge** testPtr = currentPtr - 1;
1439 while (++testPtr != lastPtr) {
1440 if ((*testPtr)->fBounds.fBottom > y) {
1441 continue;
1442 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001443 if (activeEdges) {
1444 InEdge* test = *testPtr;
1445 ActiveEdge* activePtr = activeEdges->begin() - 1;
1446 ActiveEdge* lastActive = activeEdges->end();
1447 while (++activePtr != lastActive) {
1448 if (activePtr->fWorkEdge.fEdge == test) {
1449 activeEdges->remove(activePtr - activeEdges->begin());
1450 break;
1451 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001452 }
1453 }
1454 if (testPtr == currentPtr) {
1455 ++currentPtr;
1456 }
1457 }
1458 return currentPtr;
1459}
1460
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001461// OPTIMIZE: inline?
1462static HorizontalEdge** advanceHorizontal(HorizontalEdge** edge, SkScalar y) {
1463 while ((*edge)->fY < y) {
1464 ++edge;
1465 }
1466 return edge;
1467}
1468
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001469// compute bottom taking into account any intersected edges
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001470static SkScalar computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
1471 SkScalar y, SkScalar bottom) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001472 ActiveEdge* activePtr = activeEdges.begin() - 1;
1473 ActiveEdge* lastActive = activeEdges.end();
1474 while (++activePtr != lastActive) {
1475 const InEdge* test = activePtr->fWorkEdge.fEdge;
1476 if (!test->fContainsIntercepts) {
1477 continue;
1478 }
1479 WorkEdge wt;
1480 wt.init(test);
1481 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001482 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
caryclark@google.com4917f172012-03-05 22:01:21 +00001483 if (intercepts.fTopIntercepts > 1) {
1484 SkScalar yTop = wt.fPts[0].fY;
1485 if (yTop > y && bottom > yTop) {
1486 bottom = yTop;
1487 }
1488 }
1489 if (intercepts.fBottomIntercepts > 1) {
1490 SkScalar yBottom = wt.fPts[wt.verb()].fY;
1491 if (yBottom > y && bottom > yBottom) {
1492 bottom = yBottom;
1493 }
1494 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001495 const SkTDArray<double>& fTs = intercepts.fTs;
1496 size_t count = fTs.count();
1497 for (size_t index = 0; index < count; ++index) {
1498 if (wt.verb() == SkPath::kLine_Verb) {
1499 SkScalar yIntercept = LineYAtT(wt.fPts, fTs[index]);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001500 if (yIntercept > y && bottom > yIntercept) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001501 bottom = yIntercept;
1502 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001503 } else {
1504 // FIXME: add all curve types
1505 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001506 }
1507 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001508 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001509 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001510 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001511}
1512
1513static SkScalar findBottom(InEdge** currentPtr,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001514 InEdge** edgeListEnd, SkTDArray<ActiveEdge>* activeEdges, SkScalar y,
caryclark@google.com6008c6562012-02-15 22:01:16 +00001515 bool asFill, InEdge**& testPtr) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001516 InEdge* current = *currentPtr;
1517 SkScalar bottom = current->fBounds.fBottom;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001518
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001519 // find the list of edges that cross y
caryclark@google.com6008c6562012-02-15 22:01:16 +00001520 InEdge* test = *testPtr;
1521 while (testPtr != edgeListEnd) {
1522 SkScalar testTop = test->fBounds.fTop;
1523 if (bottom <= testTop) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001524 break;
1525 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001526 SkScalar testBottom = test->fBounds.fBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001527 // OPTIMIZATION: Shortening the bottom is only interesting when filling
1528 // and when the edge is to the left of a longer edge. If it's a framing
1529 // edge, or part of the right, it won't effect the longer edges.
caryclark@google.com6008c6562012-02-15 22:01:16 +00001530 if (testTop > y) {
1531 bottom = testTop;
1532 break;
1533 }
1534 if (y < testBottom) {
1535 if (bottom > testBottom) {
1536 bottom = testBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001537 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001538 if (activeEdges) {
1539 addToActive(*activeEdges, test);
1540 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001541 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001542 test = *++testPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001543 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001544 return bottom;
1545}
1546
1547static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
1548 SkTDArray<InEdge*>& edgeList) {
1549 size_t edgeCount = edges.count();
1550 if (edgeCount == 0) {
1551 return;
1552 }
1553 for (size_t index = 0; index < edgeCount; ++index) {
1554 *edgeList.append() = &edges[index];
1555 }
1556 edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1557 *edgeList.append() = &edgeSentinel;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001558 QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001559}
1560
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001561static void makeHorizontalList(SkTDArray<HorizontalEdge>& edges,
1562 HorizontalEdge& edgeSentinel, SkTDArray<HorizontalEdge*>& edgeList) {
1563 size_t edgeCount = edges.count();
1564 if (edgeCount == 0) {
1565 return;
1566 }
1567 for (size_t index = 0; index < edgeCount; ++index) {
1568 *edgeList.append() = &edges[index];
1569 }
1570 edgeSentinel.fLeft = edgeSentinel.fRight = edgeSentinel.fY = SK_ScalarMax;
1571 *edgeList.append() = &edgeSentinel;
1572 QSort<HorizontalEdge>(edgeList.begin(), edgeList.end() - 1);
1573}
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001574
1575static void skipCoincidence(int lastWinding, int winding, int windingMask,
1576 ActiveEdge* activePtr, ActiveEdge* firstCoincident) {
1577 if (((lastWinding & windingMask) == 0) ^ (winding & windingMask) != 0) {
1578 return;
1579 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001580 // FIXME: ? shouldn't this be if (lastWinding & windingMask) ?
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001581 if (lastWinding) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001582#if DEBUG_ADJUST_COINCIDENT
1583 SkDebugf("%s edge=%d 1 set skip=false\n", __FUNCTION__, activePtr->ID());
1584#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001585 activePtr->fSkip = false;
1586 } else {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001587#if DEBUG_ADJUST_COINCIDENT
1588 SkDebugf("%s edge=%d 2 set skip=false\n", __FUNCTION__, firstCoincident->ID());
1589#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001590 firstCoincident->fSkip = false;
1591 }
1592}
1593
caryclark@google.com6008c6562012-02-15 22:01:16 +00001594static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001595 SkTDArray<ActiveEdge*>& edgeList, SkScalar y) {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001596 size_t edgeCount = activeEdges.count();
1597 if (edgeCount == 0) {
1598 return;
1599 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001600#if DEBUG_SORT_HORIZONTAL
caryclark@google.comd88e0892012-03-27 13:23:51 +00001601 const int tab = 3; // FIXME: debugging only
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001602 SkDebugf("%s y=%1.9g\n", __FUNCTION__, y);
1603#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00001604 size_t index;
1605 for (index = 0; index < edgeCount; ++index) {
1606 ActiveEdge& activeEdge = activeEdges[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001607 do {
1608 activeEdge.calcLeft(y);
1609 // skip segments that don't span y
1610 if (activeEdge.fAbove != activeEdge.fBelow) {
1611 break;
1612 }
1613 if (activeEdge.fDone) {
1614#if DEBUG_SORT_HORIZONTAL
1615 SkDebugf("%*s edge=%d done\n", tab, "", activeEdge.ID());
1616#endif
1617 goto nextEdge;
1618 }
1619#if DEBUG_SORT_HORIZONTAL
1620 SkDebugf("%*s edge=%d above==below\n", tab, "", activeEdge.ID());
1621#endif
1622 } while (activeEdge.advanceT() || activeEdge.advance());
1623#if DEBUG_SORT_HORIZONTAL
1624 SkDebugf("%*s edge=%d above=(%1.9g,%1.9g) (%1.9g) below=(%1.9g,%1.9g)"
1625 " (%1.9g)\n", tab, "", activeEdge.ID(),
1626 activeEdge.fAbove.fX, activeEdge.fAbove.fY, activeEdge.fTAbove,
1627 activeEdge.fBelow.fX, activeEdge.fBelow.fY, activeEdge.fTBelow);
1628#endif
1629 activeEdge.fSkip = activeEdge.fCloseCall = activeEdge.fFixBelow = false;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001630 *edgeList.append() = &activeEdge;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001631nextEdge:
1632 ;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001633 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001634 QSort<ActiveEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001635}
1636
1637// remove coincident edges
1638// OPTIMIZE: remove edges? This is tricky because the current logic expects
1639// the winding count to be maintained while skipping coincident edges. In
1640// addition to removing the coincident edges, the remaining edges would need
1641// to have a different winding value, possibly different per intercept span.
1642static SkScalar adjustCoincident(SkTDArray<ActiveEdge*>& edgeList,
1643 int windingMask, SkScalar y, SkScalar bottom, OutEdgeBuilder& outBuilder)
1644{
1645#if DEBUG_ADJUST_COINCIDENT
1646 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
1647#endif
1648 size_t edgeCount = edgeList.count();
1649 if (edgeCount == 0) {
1650 return bottom;
1651 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001652 ActiveEdge* activePtr = edgeList[0];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001653 size_t index;
1654 bool foundCoincident = false;
1655 int firstIndex = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001656 for (index = 1; index < edgeCount; ++index) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001657 ActiveEdge* nextPtr = edgeList[index];
1658 bool closeCall = false;
1659 activePtr->fCoincident = firstIndex;
1660 if (activePtr->isCoincidentWith(nextPtr, y)
1661 || (closeCall = activePtr->tooCloseToCall(nextPtr))) {
1662 activePtr->fSkip = nextPtr->fSkip = foundCoincident = true;
1663 activePtr->fCloseCall = nextPtr->fCloseCall = closeCall;
1664 } else {
1665 firstIndex = index;
1666 }
1667 activePtr = nextPtr;
1668 }
1669 activePtr->fCoincident = firstIndex;
1670 if (!foundCoincident) {
1671 return bottom;
1672 }
1673 int winding = 0;
1674 activePtr = edgeList[0];
1675 for (index = 1; index < edgeCount; ++index) {
1676 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001677 winding += activePtr->fWorkEdge.winding();
caryclark@google.com6008c6562012-02-15 22:01:16 +00001678 ActiveEdge* nextPtr = edgeList[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001679 if (activePtr->fCoincident == nextPtr->fCoincident) {
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001680 // the coincident edges may not have been sorted above -- advance
1681 // the edges and resort if needed
1682 // OPTIMIZE: if sorting is done incrementally as new edges are added
1683 // and not all at once as is done here, fold this test into the
1684 // current less than test.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001685 if (activePtr->fCloseCall ? activePtr->swapClose(nextPtr,
1686 priorWinding, winding, windingMask)
1687 : activePtr->swapCoincident(nextPtr, bottom)) {
caryclark@google.com4917f172012-03-05 22:01:21 +00001688 winding -= activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001689 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
1690 SkTSwap<ActiveEdge*>(activePtr, nextPtr);
caryclark@google.com4917f172012-03-05 22:01:21 +00001691 winding += activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001692 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001693 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001694 activePtr = nextPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001695 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001696 int firstCoincidentWinding = 0;
1697 ActiveEdge* firstCoincident = NULL;
1698 winding = 0;
1699 activePtr = edgeList[0];
1700 for (index = 1; index < edgeCount; ++index) {
1701 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001702 winding += activePtr->fWorkEdge.winding();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001703 ActiveEdge* nextPtr = edgeList[index];
1704 if (activePtr->fCoincident == nextPtr->fCoincident) {
1705 if (!firstCoincident) {
1706 firstCoincident = activePtr;
1707 firstCoincidentWinding = priorWinding;
1708 }
1709 if (activePtr->fCloseCall) {
1710 // If one of the edges has already been added to out as a non
1711 // coincident edge, trim it back to the top of this span
1712 if (outBuilder.trimLine(y, activePtr->ID())) {
1713 activePtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001714 #if DEBUG_ADJUST_COINCIDENT
1715 SkDebugf("%s 1 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
1716 __FUNCTION__, activePtr->ID(), y, activePtr->fYBottom);
1717 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001718 activePtr->fYBottom = y;
1719 }
1720 if (outBuilder.trimLine(y, nextPtr->ID())) {
1721 nextPtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001722 #if DEBUG_ADJUST_COINCIDENT
1723 SkDebugf("%s 2 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
1724 __FUNCTION__, nextPtr->ID(), y, nextPtr->fYBottom);
1725 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001726 nextPtr->fYBottom = y;
1727 }
1728 // add missing t values so edges can be the same length
1729 SkScalar testY = activePtr->fBelow.fY;
1730 nextPtr->addTatYBelow(testY);
1731 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001732 #if DEBUG_ADJUST_COINCIDENT
1733 SkDebugf("%s 3 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
1734 __FUNCTION__, activePtr->ID(), testY, bottom);
1735 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001736 bottom = testY;
1737 }
1738 testY = nextPtr->fBelow.fY;
1739 activePtr->addTatYBelow(testY);
1740 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001741 #if DEBUG_ADJUST_COINCIDENT
1742 SkDebugf("%s 4 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
1743 __FUNCTION__, nextPtr->ID(), testY, bottom);
1744 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001745 bottom = testY;
1746 }
1747 }
1748 } else if (firstCoincident) {
1749 skipCoincidence(firstCoincidentWinding, winding, windingMask,
1750 activePtr, firstCoincident);
1751 firstCoincident = NULL;
1752 }
1753 activePtr = nextPtr;
1754 }
1755 if (firstCoincident) {
1756 winding += activePtr->fWorkEdge.winding();
1757 skipCoincidence(firstCoincidentWinding, winding, windingMask, activePtr,
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001758 firstCoincident);
1759 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001760 // fix up the bottom for close call edges. OPTIMIZATION: maybe this could
1761 // be in the loop above, but moved here since loop above reads fBelow and
1762 // it felt unsafe to write it in that loop
1763 for (index = 0; index < edgeCount; ++index) {
1764 (edgeList[index])->fixBelow();
1765 }
1766 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001767}
1768
1769// stitch edge and t range that satisfies operation
caryclark@google.com6008c6562012-02-15 22:01:16 +00001770static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
caryclark@google.com752b60e2012-03-22 21:11:17 +00001771 SkScalar bottom, int windingMask, bool fill, OutEdgeBuilder& outBuilder) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001772 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001773 ActiveEdge** activeHandle = edgeList.begin() - 1;
1774 ActiveEdge** lastActive = edgeList.end();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001775 const int tab = 7; // FIXME: debugging only
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001776 if (gShowDebugf) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001777 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001778 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001779 while (++activeHandle != lastActive) {
1780 ActiveEdge* activePtr = *activeHandle;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001781 const WorkEdge& wt = activePtr->fWorkEdge;
1782 int lastWinding = winding;
1783 winding += wt.winding();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001784 if (gShowDebugf) {
1785 SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d"
1786#if DEBUG_ABOVE_BELOW
1787 " above=%1.9g below=%1.9g"
1788#endif
1789 "\n",
1790 tab-4, "", activePtr->ID(), lastWinding,
1791 winding, activePtr->fSkip, activePtr->fCloseCall
1792#if DEBUG_ABOVE_BELOW
1793 , activePtr->fTAbove, activePtr->fTBelow
1794#endif
1795 );
1796 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001797 if (activePtr->done(bottom)) {
1798 if (gShowDebugf) {
1799 SkDebugf("%*s fDone=%d || fYBottom=%1.9g >= bottom\n", tab, "",
1800 activePtr->fDone, activePtr->fYBottom);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001801 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001802 continue;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001803 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00001804 int opener = (lastWinding & windingMask) == 0;
1805 bool closer = (winding & windingMask) == 0;
1806 SkASSERT(!opener | !closer);
1807 bool inWinding = opener | closer;
caryclark@google.com4917f172012-03-05 22:01:21 +00001808 SkPoint clippedPts[2];
1809 const SkPoint* clipped = NULL;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001810 #if COMPARE_DOUBLE
1811 _Line dPoints;
1812 _Line clippedDPts;
1813 const _Point* dClipped = NULL;
1814 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001815 uint8_t verb = wt.verb();
1816 bool moreToDo, aboveBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001817 do {
1818 double currentT = activePtr->t();
caryclark@google.com6008c6562012-02-15 22:01:16 +00001819 SkASSERT(currentT < 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001820 const SkPoint* points = wt.fPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001821 double nextT;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001822 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001823 nextT = activePtr->nextT();
1824 if (verb == SkPath::kLine_Verb) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001825 // FIXME: obtuse: want efficient way to say
1826 // !currentT && currentT != 1 || !nextT && nextT != 1
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001827 if (currentT * nextT != 0 || currentT + nextT != 1) {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001828 // OPTIMIZATION: if !inWinding, we only need
1829 // clipped[1].fY
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001830 LineSubDivide(points, currentT, nextT, clippedPts);
1831 clipped = clippedPts;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001832 #if COMPARE_DOUBLE
1833 LineSubDivide(points, currentT, nextT, clippedDPts);
1834 dClipped = clippedDPts;
1835 #endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001836 } else {
1837 clipped = points;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001838 #if COMPARE_DOUBLE
1839 dPoints[0].x = SkScalarToDouble(points[0].fX);
1840 dPoints[0].y = SkScalarToDouble(points[1].fY);
1841 dPoints[1].x = SkScalarToDouble(points[0].fX);
1842 dPoints[1].y = SkScalarToDouble(points[1].fY);
1843 dClipped = dPoints;
1844 #endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001845 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001846 if (inWinding && !activePtr->fSkip && (fill ? clipped[0].fY
1847 != clipped[1].fY : clipped[0] != clipped[1])) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001848 if (gShowDebugf) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001849 SkDebugf("%*s line %1.9g,%1.9g %1.9g,%1.9g edge=%d"
1850 " v=%d t=(%1.9g,%1.9g)\n", tab, "",
caryclark@google.comc17972e2012-02-20 21:33:22 +00001851 clipped[0].fX, clipped[0].fY,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001852 clipped[1].fX, clipped[1].fY,
1853 activePtr->ID(),
1854 activePtr->fWorkEdge.fVerb
1855 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
1856 currentT, nextT);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001857 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001858 outBuilder.addLine(clipped,
1859 activePtr->fWorkEdge.fEdge->fID,
1860 activePtr->fCloseCall);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001861 } else {
1862 if (gShowDebugf) {
1863 SkDebugf("%*s skip %1.9g,%1.9g %1.9g,%1.9g"
1864 " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
1865 clipped[0].fX, clipped[0].fY,
1866 clipped[1].fX, clipped[1].fY,
1867 activePtr->ID(),
1868 activePtr->fWorkEdge.fVerb
1869 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
1870 currentT, nextT);
1871 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001872 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001873 // by advancing fAbove/fBelow, the next call to sortHorizontal
1874 // will use these values if they're still valid instead of
1875 // recomputing
1876 #if COMPARE_DOUBLE
1877 SkASSERT((clipped[1].fY > activePtr->fBelow.fY
1878 && bottom >= activePtr->fBelow.fY)
1879 == (dClipped[1].y > activePtr->fDBelow.y
1880 && bottom >= activePtr->fDBelow.y));
1881 #endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001882 if (clipped[1].fY > activePtr->fBelow.fY
1883 && bottom >= activePtr->fBelow.fY ) {
1884 activePtr->fAbove = activePtr->fBelow;
1885 activePtr->fBelow = clipped[1];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001886 #if COMPARE_DOUBLE
1887 activePtr->fDAbove = activePtr->fDBelow;
1888 activePtr->fDBelow = dClipped[1];
1889 #endif
1890 #if DEBUG_ABOVE_BELOW
1891 activePtr->fTAbove = activePtr->fTBelow;
1892 activePtr->fTBelow = nextT;
1893 #endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001894 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001895 } else {
1896 // FIXME: add all curve types
1897 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001898 }
1899 currentT = nextT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001900 moreToDo = activePtr->advanceT();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001901 activePtr->fYBottom = clipped[verb].fY; // was activePtr->fCloseCall ? bottom :
1902
1903 // clearing the fSkip/fCloseCall bit here means that trailing edges
1904 // fall out of sync, if one edge is long and another is a series of short pieces
1905 // if fSkip/fCloseCall is set, need to recompute coincidence/too-close-to-call
1906 // after advancing
1907 // another approach would be to restrict bottom to smaller part of close call
1908 // maybe this is already happening with coincidence when intersection is computed,
1909 // and needs to be added to the close call computation as well
1910 // this is hard to do because that the bottom is important is not known when
1911 // the lines are intersected; only when the computation for edge sorting is done
1912 // does the need for new bottoms become apparent.
1913 // maybe this is good incentive to scrap the current sort and do an insertion
1914 // sort that can take this into consideration when the x value is computed
1915
1916 // FIXME: initialized in sortHorizontal, cleared here as well so
1917 // that next edge is not skipped -- but should skipped edges ever
1918 // continue? (probably not)
caryclark@google.com752b60e2012-03-22 21:11:17 +00001919 aboveBottom = clipped[verb].fY < bottom;
1920 if (clipped[0].fY != clipped[verb].fY) {
1921 activePtr->fSkip = false;
1922 activePtr->fCloseCall = false;
1923 aboveBottom &= !activePtr->fCloseCall;
1924 } else {
1925 if (activePtr->fSkip || activePtr->fCloseCall) {
1926 SkDebugf("== %1.9g\n", clippedPts[0].fY);
1927 }
1928 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001929 } while (moreToDo & aboveBottom);
1930 } while ((moreToDo || activePtr->advance()) & aboveBottom);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001931 }
1932}
1933
caryclark@google.comc6825902012-02-03 22:07:47 +00001934void simplify(const SkPath& path, bool asFill, SkPath& simple) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001935 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
1936 int windingMask = (path.getFillType() & 1) ? 1 : -1;
1937 simple.reset();
1938 simple.setFillType(SkPath::kEvenOdd_FillType);
caryclark@google.comc6825902012-02-03 22:07:47 +00001939 // turn path into list of edges increasing in y
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001940 // if an edge is a quad or a cubic with a y extrema, note it, but leave it
1941 // unbroken. Once we have a list, sort it, then walk the list (walk edges
1942 // twice that have y extrema's on top) and detect crossings -- look for raw
1943 // bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +00001944 SkTArray<InEdge> edges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001945 SkTDArray<HorizontalEdge> horizontalEdges;
1946 InEdgeBuilder builder(path, asFill, edges, horizontalEdges);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001947 SkTDArray<InEdge*> edgeList;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001948 InEdge edgeSentinel;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001949 makeEdgeList(edges, edgeSentinel, edgeList);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001950 SkTDArray<HorizontalEdge*> horizontalList;
1951 HorizontalEdge horizontalSentinel;
1952 makeHorizontalList(horizontalEdges, horizontalSentinel, horizontalList);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001953 InEdge** currentPtr = edgeList.begin();
caryclark@google.com4917f172012-03-05 22:01:21 +00001954 if (!currentPtr) {
1955 return;
1956 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001957 // find all intersections between edges
1958// beyond looking for horizontal intercepts, we need to know if any active edges
1959// intersect edges below 'bottom', but above the active edge segment.
1960// maybe it makes more sense to compute all intercepts before doing anything
1961// else, since the intercept list is long-lived, at least in the current design.
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001962 SkScalar y = (*currentPtr)->fBounds.fTop;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001963 HorizontalEdge** currentHorizontal = horizontalList.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00001964 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001965 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001966 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001967 NULL, y, asFill, lastPtr);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001968 if (lastPtr > currentPtr) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001969 if (currentHorizontal) {
1970 if ((*currentHorizontal)->fY < SK_ScalarMax) {
1971 addBottomT(currentPtr, lastPtr, currentHorizontal);
1972 }
1973 currentHorizontal = advanceHorizontal(currentHorizontal, bottom);
1974 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00001975 addIntersectingTs(currentPtr, lastPtr);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001976 }
1977 y = bottom;
1978 currentPtr = advanceEdges(NULL, currentPtr, lastPtr, y);
1979 } while (*currentPtr != &edgeSentinel);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001980
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001981#if DEBUG_DUMP
1982 InEdge** debugPtr = edgeList.begin();
1983 do {
1984 (*debugPtr++)->dump();
1985 } while (*debugPtr != &edgeSentinel);
1986#endif
caryclark@google.com752b60e2012-03-22 21:11:17 +00001987
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001988 // walk the sorted edges from top to bottom, computing accumulated winding
1989 SkTDArray<ActiveEdge> activeEdges;
1990 OutEdgeBuilder outBuilder(asFill);
1991 currentPtr = edgeList.begin();
1992 y = (*currentPtr)->fBounds.fTop;
1993 do {
1994 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
1995 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
1996 &activeEdges, y, asFill, lastPtr);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001997#if DEBUG_BOTTOM
1998 SkDebugf("%s findBottom bottom=%1.9g\n", __FUNCTION__, bottom);
1999#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002000 if (lastPtr > currentPtr) {
2001 bottom = computeInterceptBottom(activeEdges, y, bottom);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002002#if DEBUG_BOTTOM
2003 SkDebugf("%s computeInterceptBottom bottom=%1.9g\n", __FUNCTION__, bottom);
2004#endif
caryclark@google.comc17972e2012-02-20 21:33:22 +00002005 SkTDArray<ActiveEdge*> activeEdgeList;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002006 sortHorizontal(activeEdges, activeEdgeList, y);
2007 bottom = adjustCoincident(activeEdgeList, windingMask, y, bottom,
2008 outBuilder);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002009#if DEBUG_BOTTOM
2010 SkDebugf("%s adjustCoincident bottom=%1.9g\n", __FUNCTION__, bottom);
2011#endif
2012 stitchEdge(activeEdgeList, y, bottom, windingMask, asFill, outBuilder);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002013 }
caryclark@google.comc6825902012-02-03 22:07:47 +00002014 y = bottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002015 // OPTIMIZATION: as edges expire, InEdge allocations could be released
2016 currentPtr = advanceEdges(&activeEdges, currentPtr, lastPtr, y);
caryclark@google.comc6825902012-02-03 22:07:47 +00002017 } while (*currentPtr != &edgeSentinel);
caryclark@google.comc6825902012-02-03 22:07:47 +00002018 // assemble output path from string of pts, verbs
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002019 outBuilder.bridge();
2020 outBuilder.assemble(simple);
caryclark@google.comc6825902012-02-03 22:07:47 +00002021}