blob: 94d6e5a2a97340638e28c137cbda85f30eaa7d54 [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"
15#include "TSearch.h"
16
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000017#if 0 // set to 1 for no debugging whatsoever
caryclark@google.com4917f172012-03-05 22:01:21 +000018static bool gShowDebugf = false; // FIXME: remove once debugging is complete
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000019
20#define DEBUG_DUMP 0
21#define DEBUG_ADD 0
22#define DEBUG_ADD_INTERSECTING_TS 0
23#define DEBUG_ADD_BOTTOM_TS 0
24#define COMPARE_DOUBLE 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000025#define DEBUG_ABOVE_BELOW 0
26#define DEBUG_ACTIVE_LESS_THAN 0
27#define DEBUG_SORT_HORIZONTAL 0
28#define DEBUG_OUT 0
29#define DEBUG_OUT_LESS_THAN 0
30#define DEBUG_ADJUST_COINCIDENT 0
caryclark@google.com752b60e2012-03-22 21:11:17 +000031#define DEBUG_BOTTOM 0
32
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000033#else
34static bool gShowDebugf = true; // FIXME: remove once debugging is complete
35
36#define DEBUG_DUMP 01
37#define DEBUG_ADD 01
38#define DEBUG_ADD_INTERSECTING_TS 0
39#define DEBUG_ADD_BOTTOM_TS 0
caryclark@google.com752b60e2012-03-22 21:11:17 +000040#define COMPARE_DOUBLE 01
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000041#define DEBUG_ABOVE_BELOW 01
caryclark@google.com752b60e2012-03-22 21:11:17 +000042#define DEBUG_ACTIVE_LESS_THAN 01
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000043#define DEBUG_SORT_HORIZONTAL 01
44#define DEBUG_OUT 01
45#define DEBUG_OUT_LESS_THAN 0
46#define DEBUG_ADJUST_COINCIDENT 1
caryclark@google.com752b60e2012-03-22 21:11:17 +000047#define DEBUG_BOTTOM 01
48
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000049#endif
50
51// FIXME: not wild about this -- min delta should be based on size of curve, not t
52// #define MIN_T_DELTA 0.000001
53// not wild about this either -- for SkScalars backed by floats, would like to
54// represent deltas in terms of number of significant matching bits
55#define MIN_PT_DELTA 0.000001
caryclark@google.comc17972e2012-02-20 21:33:22 +000056
caryclark@google.com6680fb12012-02-07 22:10:51 +000057static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
caryclark@google.comc6825902012-02-03 22:07:47 +000058 double aRange[2], double bRange[2]) {
59 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
60 _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
61 return intersect(aLine, bLine, aRange, bRange);
62}
63
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000064static int LineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
65 SkScalar y, double aRange[2]) {
caryclark@google.comc6825902012-02-03 22:07:47 +000066 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000067 return horizontalLineIntersect(aLine, left, right, y, aRange);
caryclark@google.comc6825902012-02-03 22:07:47 +000068}
69
caryclark@google.comcd4421d2012-03-01 19:16:31 +000070static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +000071 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.comcd4421d2012-03-01 19:16:31 +000072 double x, y;
73 xy_at_t(aLine, t, x, y);
74 out->fX = SkDoubleToScalar(x);
75 out->fY = SkDoubleToScalar(y);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +000076}
77
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000078#if COMPARE_DOUBLE
79static void LineXYAtT(const SkPoint a[2], double t, _Point* out) {
80 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
81 xy_at_t(aLine, t, out->x, out->y);
82}
83#endif
84
85#if 0 // unused for now
86static SkScalar LineXAtT(const SkPoint a[2], double t) {
87 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
88 double x;
89 xy_at_t(aLine, t, x, *(double*) 0);
90 return SkDoubleToScalar(x);
91}
92#endif
93
caryclark@google.com6680fb12012-02-07 22:10:51 +000094static SkScalar LineYAtT(const SkPoint a[2], double t) {
95 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
96 double y;
97 xy_at_t(aLine, t, *(double*) 0, y);
98 return SkDoubleToScalar(y);
99}
100
101static void LineSubDivide(const SkPoint a[2], double startT, double endT,
102 SkPoint sub[2]) {
103 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
104 _Line dst;
105 sub_divide(aLine, startT, endT, dst);
106 sub[0].fX = SkDoubleToScalar(dst[0].x);
107 sub[0].fY = SkDoubleToScalar(dst[0].y);
108 sub[1].fX = SkDoubleToScalar(dst[1].x);
109 sub[1].fY = SkDoubleToScalar(dst[1].y);
110}
111
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000112#if COMPARE_DOUBLE
113static void LineSubDivide(const SkPoint a[2], double startT, double endT,
114 _Line& dst) {
115 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
116 sub_divide(aLine, startT, endT, dst);
117}
118#endif
caryclark@google.com6680fb12012-02-07 22:10:51 +0000119
caryclark@google.comc6825902012-02-03 22:07:47 +0000120// functions
121void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray);
122void simplify(const SkPath& path, bool asFill, SkPath& simple);
123/*
124list of edges
125bounds for edge
126sort
127active T
128
129if a contour's bounds is outside of the active area, no need to create edges
130*/
131
132/* given one or more paths,
133 find the bounds of each contour, select the active contours
134 for each active contour, compute a set of edges
135 each edge corresponds to one or more lines and curves
136 leave edges unbroken as long as possible
137 when breaking edges, compute the t at the break but leave the control points alone
138
139 */
140
141void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray) {
142 SkPath::Iter iter(path, false);
143 SkPoint pts[4];
144 SkPath::Verb verb;
145 SkRect bounds;
146 bounds.setEmpty();
147 int count = 0;
148 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
149 switch (verb) {
150 case SkPath::kMove_Verb:
151 if (!bounds.isEmpty()) {
152 *boundsArray.append() = bounds;
153 }
154 bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
155 count = 0;
156 break;
157 case SkPath::kLine_Verb:
158 count = 1;
159 break;
160 case SkPath::kQuad_Verb:
161 count = 2;
162 break;
163 case SkPath::kCubic_Verb:
164 count = 3;
165 break;
166 case SkPath::kClose_Verb:
167 count = 0;
168 break;
169 default:
170 SkDEBUGFAIL("bad verb");
171 return;
172 }
173 for (int i = 1; i <= count; ++i) {
174 bounds.growToInclude(pts[i].fX, pts[i].fY);
175 }
176 }
177}
178
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000179static bool extendLine(const SkPoint line[2], const SkPoint& add) {
180 // FIXME: allow this to extend lines that have slopes that are nearly equal
181 SkScalar dx1 = line[1].fX - line[0].fX;
182 SkScalar dy1 = line[1].fY - line[0].fY;
183 SkScalar dx2 = add.fX - line[0].fX;
184 SkScalar dy2 = add.fY - line[0].fY;
185 return dx1 * dy2 == dx2 * dy1;
186}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000187
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000188// OPTIMIZATION: this should point to a list of input data rather than duplicating
189// the line data here. This would reduce the need to assemble the results.
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000190struct OutEdge {
191 bool operator<(const OutEdge& rh) const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000192 const SkPoint& first = fPts[0];
193 const SkPoint& rhFirst = rh.fPts[0];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000194 return first.fY == rhFirst.fY
195 ? first.fX < rhFirst.fX
196 : first.fY < rhFirst.fY;
197 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000198
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000199 SkPoint fPts[4];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000200 int fID; // id of edge generating data
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000201 uint8_t fVerb; // FIXME: not read from everywhere
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000202 bool fCloseCall; // edge is trimmable if not originally coincident
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000203};
204
caryclark@google.com6680fb12012-02-07 22:10:51 +0000205class OutEdgeBuilder {
206public:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000207 OutEdgeBuilder(bool fill)
208 : fFill(fill) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000209 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000210
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000211 void addLine(const SkPoint line[2], int id, bool closeCall) {
caryclark@google.comc17972e2012-02-20 21:33:22 +0000212 OutEdge& newEdge = fEdges.push_back();
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000213 newEdge.fPts[0] = line[0];
214 newEdge.fPts[1] = line[1];
215 newEdge.fVerb = SkPath::kLine_Verb;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000216 newEdge.fID = id;
217 newEdge.fCloseCall = closeCall;
218 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000219
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000220 bool trimLine(SkScalar y, int id) {
221 size_t count = fEdges.count();
222 while (count-- != 0) {
223 OutEdge& edge = fEdges[count];
224 if (edge.fID != id) {
225 continue;
226 }
227 if (edge.fCloseCall) {
228 return false;
229 }
230 SkASSERT(edge.fPts[0].fY <= y);
231 if (edge.fPts[1].fY <= y) {
232 continue;
233 }
234 edge.fPts[1].fX = edge.fPts[0].fX + (y - edge.fPts[0].fY)
235 * (edge.fPts[1].fX - edge.fPts[0].fX)
236 / (edge.fPts[1].fY - edge.fPts[0].fY);
237 edge.fPts[1].fY = y;
238 if (gShowDebugf) {
239 SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id,
240 edge.fPts[1].fX, y);
241 }
242 return true;
243 }
244 return false;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000245 }
246
247 void assemble(SkPath& simple) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000248 size_t listCount = fEdges.count();
249 if (listCount == 0) {
250 return;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000251 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000252 do {
253 size_t listIndex = 0;
254 int advance = 1;
255 while (listIndex < listCount && fTops[listIndex] == 0) {
256 ++listIndex;
257 }
258 if (listIndex >= listCount) {
259 break;
260 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000261 int closeEdgeIndex = -listIndex - 1;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000262 SkPoint firstPt, lastLine[2];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000263 bool doMove = true;
264 int edgeIndex;
265 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000266 SkPoint* ptArray = fEdges[listIndex].fPts;
267 uint8_t verb = fEdges[listIndex].fVerb;
268 SkPoint* start, * end;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000269 if (advance < 0) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000270 start = &ptArray[verb];
271 end = &ptArray[0];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000272 } else {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000273 start = &ptArray[0];
274 end = &ptArray[verb];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000275 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000276 switch (verb) {
277 case SkPath::kLine_Verb:
278 bool gap;
279 if (doMove) {
280 firstPt = *start;
281 simple.moveTo(start->fX, start->fY);
282 if (gShowDebugf) {
283 SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
284 start->fX, start->fY);
285 }
286 lastLine[0] = *start;
287 lastLine[1] = *end;
288 doMove = false;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000289 break;
290 }
291 gap = lastLine[1] != *start;
292 if (gap) {
293 SkASSERT(fFill && lastLine[1].fY == start->fY);
294 simple.lineTo(lastLine[1].fX, lastLine[1].fY);
295 if (gShowDebugf) {
296 SkDebugf("%s lineTo x (%g,%g)\n", __FUNCTION__,
297 lastLine[1].fX, lastLine[1].fY);
298 }
299 }
300 if (gap || !extendLine(lastLine, *end)) {
301 SkASSERT(lastLine[1] == *start ||
302 (fFill && lastLine[1].fY == start->fY));
303 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
450 if (left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY
451 != right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) {
452 *fMismatches.append() = leftIndex;
453 if (rightPtr == lastPtr) {
454 *fMismatches.append() = rightIndex;
455 }
456 pairUp = false;
457 }
458 #else
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000459 SkASSERT(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY
460 == right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000461 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000462 }
463 if (pairUp) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000464 if (leftIndex < 0) {
465 fTops[leftOutIndex] = rightIndex;
466 } else {
467 fBottoms[leftOutIndex] = rightIndex;
468 }
469 if (rightIndex < 0) {
470 fTops[rightOutIndex] = leftIndex;
471 } else {
472 fBottoms[rightOutIndex] = leftIndex;
473 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000474 ++rightPtr;
475 }
476 leftPtr = rightPtr;
477 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000478#if DEBUG_OUT
479 int* mismatch = fMismatches.begin();
480 while (mismatch != fMismatches.end()) {
481 int leftIndex = *mismatch++;
482 int leftOutIndex = abs(leftIndex) - 1;
483 const OutEdge& left = fEdges[leftOutIndex];
484 const SkPoint& leftPt = left.fPts[leftIndex < 0 ? 0 : left.fVerb];
485 SkDebugf("%s left=%d %s (%1.9g,%1.9g)\n",
486 __FUNCTION__, left.fID, leftIndex < 0 ? "top" : "bot",
487 leftPt.fX, leftPt.fY);
488 }
489 SkASSERT(fMismatches.count() == 0);
490#endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000491 }
492
caryclark@google.com6008c6562012-02-15 22:01:16 +0000493protected:
caryclark@google.com6680fb12012-02-07 22:10:51 +0000494 SkTArray<OutEdge> fEdges;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000495 SkTDArray<int> fTops;
496 SkTDArray<int> fBottoms;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000497 bool fFill;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000498#if DEBUG_OUT
499 SkTDArray<int> fMismatches;
500#endif
caryclark@google.com6680fb12012-02-07 22:10:51 +0000501};
502
caryclark@google.comc6825902012-02-03 22:07:47 +0000503// Bounds, unlike Rect, does not consider a vertical line to be empty.
504struct Bounds : public SkRect {
505 static bool Intersects(const Bounds& a, const Bounds& b) {
506 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
507 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
508 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000509
caryclark@google.com6008c6562012-02-15 22:01:16 +0000510 bool isEmpty() {
511 return fLeft > fRight || fTop > fBottom
512 || fLeft == fRight && fTop == fBottom
513 || isnan(fLeft) || isnan(fRight)
514 || isnan(fTop) || isnan(fBottom);
515 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000516};
517
caryclark@google.com4917f172012-03-05 22:01:21 +0000518class Intercepts {
519public:
520 Intercepts()
521 : fTopIntercepts(0)
522 , fBottomIntercepts(0) {
523 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000524
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000525#if DEBUG_DUMP
526 // FIXME: pass current verb as parameter
527 void dump(const SkPoint* pts) {
528 const char className[] = "Intercepts";
529 const int tab = 8;
530 for (int i = 0; i < fTs.count(); ++i) {
531 SkPoint out;
532 LineXYAtT(pts, fTs[i], &out);
caryclark@google.com752b60e2012-03-22 21:11:17 +0000533 SkDebugf("%*s.fTs[%d]=%1.9g (%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000534 className, i, fTs[i], out.fX, out.fY);
535 }
536 SkDebugf("%*s.fTopIntercepts=%d\n", tab + sizeof(className),
537 className, fTopIntercepts);
538 SkDebugf("%*s.fBottomIntercepts=%d\n", tab + sizeof(className),
539 className, fBottomIntercepts);
540 }
541#endif
542
caryclark@google.comc6825902012-02-03 22:07:47 +0000543 SkTDArray<double> fTs;
caryclark@google.com4917f172012-03-05 22:01:21 +0000544 int fTopIntercepts;
545 int fBottomIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000546};
547
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000548struct HorizontalEdge {
549 bool operator<(const HorizontalEdge& rh) const {
550 return fY == rh.fY ? fLeft == rh.fLeft ? fRight < rh.fRight
551 : fLeft < rh.fLeft : fY < rh.fY;
552 }
553
554#if DEBUG_DUMP
555 void dump() {
556 const char className[] = "HorizontalEdge";
557 const int tab = 4;
caryclark@google.com752b60e2012-03-22 21:11:17 +0000558 SkDebugf("%*s.fLeft=%1.9g\n", tab + sizeof(className), className, fLeft);
559 SkDebugf("%*s.fRight=%1.9g\n", tab + sizeof(className), className, fRight);
560 SkDebugf("%*s.fY=%1.9g\n", tab + sizeof(className), className, fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000561 }
562#endif
563
564 SkScalar fLeft;
565 SkScalar fRight;
566 SkScalar fY;
567};
568
caryclark@google.com6680fb12012-02-07 22:10:51 +0000569struct InEdge {
570 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000571 return fBounds.fTop == rh.fBounds.fTop
572 ? fBounds.fLeft < rh.fBounds.fLeft
573 : fBounds.fTop < rh.fBounds.fTop;
574 }
575
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000576 // Avoid collapsing t values that are close to the same since
577 // we walk ts to describe consecutive intersections. Since a pair of ts can
578 // be nearly equal, any problems caused by this should be taken care
579 // of later.
580 int add(double* ts, size_t count, ptrdiff_t verbIndex) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000581 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
caryclark@google.com6008c6562012-02-15 22:01:16 +0000582 bool foundIntercept = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000583 int insertedAt = -1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000584 Intercepts& intercepts = fIntercepts[verbIndex];
caryclark@google.comc6825902012-02-03 22:07:47 +0000585 for (size_t index = 0; index < count; ++index) {
586 double t = ts[index];
caryclark@google.com4917f172012-03-05 22:01:21 +0000587 if (t <= 0) {
588 fContainsIntercepts |= ++intercepts.fTopIntercepts > 1;
589 continue;
590 }
591 if (t >= 1) {
592 fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000593 continue;
594 }
595 foundIntercept = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000596 size_t tCount = intercepts.fTs.count();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000597 double delta;
caryclark@google.comc6825902012-02-03 22:07:47 +0000598 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
599 if (t <= intercepts.fTs[idx2]) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000600 // FIXME: ? if (t < intercepts.fTs[idx2]) // failed
601 delta = intercepts.fTs[idx2] - t;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000602 if (delta > 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000603 insertedAt = idx2;
caryclark@google.comc6825902012-02-03 22:07:47 +0000604 *intercepts.fTs.insert(idx2) = t;
caryclark@google.comc6825902012-02-03 22:07:47 +0000605 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000606 goto nextPt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000607 }
608 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000609 if (tCount == 0 || (delta = t - intercepts.fTs[tCount - 1]) > 0) {
610 insertedAt = tCount;
caryclark@google.comc6825902012-02-03 22:07:47 +0000611 *intercepts.fTs.append() = t;
612 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000613 nextPt:
614 ;
caryclark@google.comc6825902012-02-03 22:07:47 +0000615 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000616 fContainsIntercepts |= foundIntercept;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000617 return insertedAt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000618 }
619
caryclark@google.com6680fb12012-02-07 22:10:51 +0000620 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000621 // FIXME: in the pathological case where there is a ton of edges, binary search?
622 size_t count = fCached.count();
623 for (size_t index = 0; index < count; ++index) {
624 if (edge == fCached[index]) {
625 return true;
626 }
627 if (edge < fCached[index]) {
628 *fCached.insert(index) = edge;
629 return false;
630 }
631 }
632 *fCached.append() = edge;
633 return false;
634 }
635
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000636 void complete(signed char winding, int id) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000637 SkPoint* ptPtr = fPts.begin();
638 SkPoint* ptLast = fPts.end();
639 if (ptPtr == ptLast) {
640 SkDebugf("empty edge\n");
641 SkASSERT(0);
642 // FIXME: delete empty edge?
643 return;
644 }
645 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
646 ++ptPtr;
647 while (ptPtr != ptLast) {
648 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
649 ++ptPtr;
650 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000651 fIntercepts.push_back_n(fVerbs.count());
caryclark@google.com6680fb12012-02-07 22:10:51 +0000652 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
653 size_t index;
654 size_t last = fPts.count() - 1;
655 for (index = 0; index < last; ++index, --last) {
656 SkTSwap<SkPoint>(fPts[index], fPts[last]);
657 }
658 last = fVerbs.count() - 1;
659 for (index = 0; index < last; ++index, --last) {
660 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
661 }
662 }
663 fContainsIntercepts = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000664 fID = id;
caryclark@google.comc6825902012-02-03 22:07:47 +0000665 }
666
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000667#if DEBUG_DUMP
668 void dump() {
669 int i;
670 const char className[] = "InEdge";
671 const int tab = 4;
672 SkDebugf("InEdge %p (edge=%d)\n", this, fID);
673 for (i = 0; i < fCached.count(); ++i) {
674 SkDebugf("%*s.fCached[%d]=0x%08x\n", tab + sizeof(className),
675 className, i, fCached[i]);
676 }
677 uint8_t* verbs = fVerbs.begin();
678 SkPoint* pts = fPts.begin();
679 for (i = 0; i < fIntercepts.count(); ++i) {
680 SkDebugf("%*s.fIntercepts[%d]:\n", tab + sizeof(className),
681 className, i);
682 // FIXME: take current verb into consideration
683 fIntercepts[i].dump(pts);
684 pts += *verbs++;
685 }
686 for (i = 0; i < fPts.count(); ++i) {
caryclark@google.com752b60e2012-03-22 21:11:17 +0000687 SkDebugf("%*s.fPts[%d]=(%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000688 className, i, fPts[i].fX, fPts[i].fY);
689 }
690 for (i = 0; i < fVerbs.count(); ++i) {
691 SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className),
692 className, i, fVerbs[i]);
693 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000694 SkDebugf("%*s.fBounds=(%1.9g. %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000695 className, fBounds.fLeft, fBounds.fTop,
696 fBounds.fRight, fBounds.fBottom);
697 SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className,
698 fWinding);
699 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
700 className, fContainsIntercepts);
701 }
702#endif
703
caryclark@google.comc6825902012-02-03 22:07:47 +0000704 // temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +0000705 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +0000706 SkTArray<Intercepts> fIntercepts; // one per verb
caryclark@google.com4917f172012-03-05 22:01:21 +0000707
caryclark@google.comc6825902012-02-03 22:07:47 +0000708 // persistent data
709 SkTDArray<SkPoint> fPts;
710 SkTDArray<uint8_t> fVerbs;
711 Bounds fBounds;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000712 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +0000713 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000714 bool fContainsIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000715};
716
caryclark@google.com6680fb12012-02-07 22:10:51 +0000717class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +0000718public:
719
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000720InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges,
721 SkTDArray<HorizontalEdge>& horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000722 : fPath(path)
723 , fCurrentEdge(NULL)
724 , fEdges(edges)
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000725 , fID(0)
726 , fHorizontalEdges(horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000727 , fIgnoreHorizontal(ignoreHorizontal)
728{
729 walk();
730}
731
732protected:
733
734void addEdge() {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000735 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +0000736 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
737 fPtOffset = 1;
738 *fCurrentEdge->fVerbs.append() = fVerb;
739}
740
caryclark@google.com6008c6562012-02-15 22:01:16 +0000741bool complete() {
742 if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000743 fCurrentEdge->complete(fWinding, ++fID);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000744 fCurrentEdge = NULL;
745 return true;
746 }
747 return false;
748}
749
caryclark@google.comc6825902012-02-03 22:07:47 +0000750int direction(int count) {
751 fPtCount = count;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000752 if (fIgnoreHorizontal && isHorizontal()) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000753 return 0;
754 }
755 int last = count - 1;
756 return fPts[0].fY == fPts[last].fY
caryclark@google.com6680fb12012-02-07 22:10:51 +0000757 ? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX
758 ? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +0000759}
760
761bool isHorizontal() {
762 SkScalar y = fPts[0].fY;
763 for (int i = 1; i < fPtCount; ++i) {
764 if (fPts[i].fY != y) {
765 return false;
766 }
767 }
768 return true;
769}
770
771void startEdge() {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000772 if (!fCurrentEdge) {
773 fCurrentEdge = fEdges.push_back_n(1);
774 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000775 fWinding = 0;
776 fPtOffset = 0;
777}
778
779void walk() {
780 SkPath::Iter iter(fPath, true);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000781 int winding = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +0000782 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
783 switch (fVerb) {
784 case SkPath::kMove_Verb:
caryclark@google.comc6825902012-02-03 22:07:47 +0000785 startEdge();
786 continue;
787 case SkPath::kLine_Verb:
788 winding = direction(2);
789 break;
790 case SkPath::kQuad_Verb:
791 winding = direction(3);
792 break;
793 case SkPath::kCubic_Verb:
794 winding = direction(4);
795 break;
796 case SkPath::kClose_Verb:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000797 SkASSERT(fCurrentEdge);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000798 complete();
caryclark@google.comc6825902012-02-03 22:07:47 +0000799 continue;
800 default:
801 SkDEBUGFAIL("bad verb");
802 return;
803 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000804 if (winding == 0) {
805 HorizontalEdge* horizontalEdge = fHorizontalEdges.append();
806 // FIXME: for degenerate quads and cubics, compute x extremes
807 horizontalEdge->fLeft = fPts[0].fX;
808 horizontalEdge->fRight = fPts[fVerb].fX;
809 horizontalEdge->fY = fPts[0].fY;
810 if (horizontalEdge->fLeft > horizontalEdge->fRight) {
811 SkTSwap<SkScalar>(horizontalEdge->fLeft, horizontalEdge->fRight);
812 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000813 if (complete()) {
814 startEdge();
815 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000816 continue;
817 }
818 if (fWinding + winding == 0) {
819 // FIXME: if prior verb or this verb is a horizontal line, reverse
820 // it instead of starting a new edge
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000821 SkASSERT(fCurrentEdge);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000822 if (complete()) {
823 startEdge();
824 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000825 }
826 fWinding = winding;
827 addEdge();
828 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +0000829 if (!complete()) {
830 if (fCurrentEdge) {
831 fEdges.pop_back();
832 }
833 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000834}
835
836private:
837 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000838 InEdge* fCurrentEdge;
839 SkTArray<InEdge>& fEdges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000840 SkTDArray<HorizontalEdge>& fHorizontalEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +0000841 SkPoint fPts[4];
842 SkPath::Verb fVerb;
843 int fPtCount;
844 int fPtOffset;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000845 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +0000846 int8_t fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000847 bool fIgnoreHorizontal;
848};
849
caryclark@google.com6680fb12012-02-07 22:10:51 +0000850struct WorkEdge {
851 SkScalar bottom() const {
852 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +0000853 }
854
caryclark@google.com6680fb12012-02-07 22:10:51 +0000855 void init(const InEdge* edge) {
856 fEdge = edge;
857 fPts = edge->fPts.begin();
858 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +0000859 }
860
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000861 bool advance() {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000862 SkASSERT(fVerb < fEdge->fVerbs.end());
863 fPts += *fVerb++;
864 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +0000865 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000866
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000867 SkPath::Verb lastVerb() const {
868 SkASSERT(fVerb > fEdge->fVerbs.begin());
869 return (SkPath::Verb) fVerb[-1];
870 }
871
caryclark@google.comc6825902012-02-03 22:07:47 +0000872
873 SkPath::Verb verb() const {
874 return (SkPath::Verb) *fVerb;
875 }
876
caryclark@google.com6008c6562012-02-15 22:01:16 +0000877 ptrdiff_t verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000878 return fVerb - fEdge->fVerbs.begin();
879 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000880
caryclark@google.com6680fb12012-02-07 22:10:51 +0000881 int winding() const {
882 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000883 }
884
caryclark@google.com6680fb12012-02-07 22:10:51 +0000885 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +0000886 const SkPoint* fPts;
887 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +0000888};
889
caryclark@google.com6680fb12012-02-07 22:10:51 +0000890// always constructed with SkTDArray because new edges are inserted
891// this may be a inappropriate optimization, suggesting that a separate array of
892// ActiveEdge* may be faster to insert and search
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000893
894// OPTIMIZATION: Brian suggests that global sorting should be unnecessary, since
895// as active edges are introduced, only local sorting should be required
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000896class ActiveEdge {
897public:
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000898 // OPTIMIZATION: fold return statements into one
caryclark@google.com6008c6562012-02-15 22:01:16 +0000899 bool operator<(const ActiveEdge& rh) const {
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000900 if (rh.fAbove.fY - fAbove.fY > fBelow.fY - rh.fAbove.fY
901 && fBelow.fY < rh.fBelow.fY
902 || fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - fAbove.fY
903 && rh.fBelow.fY < fBelow.fY) {
904 // FIXME: need to compute distance, not check for points equal
905 const SkPoint& check = rh.fBelow.fY <= fBelow.fY
906 && fBelow != rh.fBelow ? rh.fBelow :
907 rh.fAbove;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000908 #if DEBUG_ACTIVE_LESS_THAN
909 SkDebugf("%s 1 %c %cthis (edge=%d) {%g,%g %g,%g}"
910 " < rh (edge=%d) {%g,%g %g,%g} upls=(%d)\n", __FUNCTION__,
911 rh.fBelow.fY <= fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
912 (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
913 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX) ? ' '
914 : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
915 rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
916 UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
917 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX)));
918 #endif
caryclark@google.com752b60e2012-03-22 21:11:17 +0000919 #if COMPARE_DOUBLE
920 SkASSERT(((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
921 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX))
922 == ((check.fY - fDAbove.y) * (fDBelow.x - fDAbove.x)
923 < (fDBelow.y - fDAbove.y) * (check.fX - fDAbove.x)));
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000924 #endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000925 return (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
926 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX);
927 }
928 // FIXME: need to compute distance, not check for points equal
929 const SkPoint& check = fBelow.fY <= rh.fBelow.fY
930 && fBelow != rh.fBelow ? fBelow : fAbove;
caryclark@google.com752b60e2012-03-22 21:11:17 +0000931 #if DEBUG_ACTIVE_LESS_THAN
932 SkDebugf("%s 2 %c %cthis (edge=%d) {%g,%g %g,%g}"
933 " < rh (edge=%d) {%g,%g %g,%g} upls=(%d) (%d,%d)\n", __FUNCTION__,
934 fBelow.fY <= rh.fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
935 (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
936 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.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((rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX),
940 (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)),
941 UlpsDiff(fBelow.fX, rh.fBelow.fX), UlpsDiff(fBelow.fY, rh.fBelow.fY));
942 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000943 #if COMPARE_DOUBLE
944 SkASSERT(((rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
945 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX))
946 == ((rh.fDBelow.y - rh.fDAbove.y) * (check.fX - rh.fDAbove.x)
947 < (check.fY - rh.fDAbove.y) * (rh.fDBelow.x - rh.fDAbove.x)));
948 #endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000949 return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
950 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000951 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000952
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000953 // If a pair of edges are nearly coincident for some span, add a T in the
954 // edge so it can be shortened to match the other edge. Note that another
955 // approach is to trim the edge after it is added to the OutBuilder list --
956 // FIXME: since this has no effect if the edge is already done (i.e.,
957 // fYBottom >= y) maybe this can only be done by calling trimLine later.
958 void addTatYBelow(SkScalar y) {
959 if (fBelow.fY <= y || fYBottom >= y) {
960 return;
961 }
962 addTatYInner(y);
963 fFixBelow = true;
964 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000965
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000966 void addTatYAbove(SkScalar y) {
967 if (fBelow.fY <= y) {
968 return;
969 }
970 addTatYInner(y);
971 }
972
973 void addTatYInner(SkScalar y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +0000974 if (fWorkEdge.fPts[0].fY > y) {
975 backup(y);
976 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000977 SkScalar left = fWorkEdge.fPts[0].fX;
978 SkScalar right = fWorkEdge.fPts[1].fX;
979 if (left > right) {
980 SkTSwap(left, right);
981 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000982 double ts[2];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000983 int pts = LineIntersect(fWorkEdge.fPts, left, right, y, ts);
984 SkASSERT(pts == 1);
985 // An ActiveEdge or WorkEdge has no need to modify the T values computed
986 // in the InEdge, except in the following case. If a pair of edges are
987 // nearly coincident, this may not be detected when the edges are
988 // intersected. Later, when sorted, and this near-coincidence is found,
989 // an additional t value must be added, requiring the cast below.
990 InEdge* writable = const_cast<InEdge*>(fWorkEdge.fEdge);
991 int insertedAt = writable->add(ts, pts, fWorkEdge.verbIndex());
caryclark@google.com752b60e2012-03-22 21:11:17 +0000992 #if DEBUG_ADJUST_COINCIDENT
993 SkDebugf("%s edge=%d y=%1.9g t=%1.9g\n", __FUNCTION__, ID(), y, ts[0]);
994 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000995 if (insertedAt >= 0) {
996 if (insertedAt + 1 < fTIndex) {
997 SkASSERT(insertedAt + 2 == fTIndex);
998 --fTIndex;
999 }
1000 }
1001 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001002
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001003 bool advanceT() {
1004 SkASSERT(fTIndex <= fTs->count());
1005 return ++fTIndex <= fTs->count();
1006 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001007
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001008 bool advance() {
1009 // FIXME: flip sense of next
1010 bool result = fWorkEdge.advance();
1011 fDone = !result;
1012 initT();
1013 return result;
1014 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001015
1016 void backup(SkScalar y) {
1017 do {
1018 SkASSERT(fWorkEdge.fEdge->fVerbs.begin() < fWorkEdge.fVerb);
1019 fWorkEdge.fPts -= *--fWorkEdge.fVerb;
1020 SkASSERT(fWorkEdge.fEdge->fPts.begin() <= fWorkEdge.fPts);
1021 } while (fWorkEdge.fPts[0].fY >= y);
1022 initT();
1023 fTIndex = fTs->count() + 1;
1024 }
1025
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001026 void calcLeft(SkScalar y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001027 // OPTIMIZE: put a kDone_Verb at the end of the verb list?
caryclark@google.com4917f172012-03-05 22:01:21 +00001028 if (fDone || fBelow.fY > y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001029 return; // nothing to do; use last
caryclark@google.com4917f172012-03-05 22:01:21 +00001030 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001031 calcLeft();
caryclark@google.com752b60e2012-03-22 21:11:17 +00001032 if (fAbove.fY == fBelow.fY) {
1033 SkDebugf("%s edge=%d fAbove.fY != fBelow.fY %1.9g\n", __FUNCTION__,
1034 ID(), fAbove.fY);
1035 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001036 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001037
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001038 void calcLeft() {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001039 switch (fWorkEdge.verb()) {
1040 case SkPath::kLine_Verb: {
1041 // OPTIMIZATION: if fXAbove, fXBelow have already been computed
1042 // for the fTIndex, don't do it again
1043 // For identical x, this lets us know which edge is first.
1044 // If both edges have T values < 1, check x at next T (fXBelow).
1045 int add = (fTIndex <= fTs->count()) - 1;
1046 double tAbove = t(fTIndex + add);
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001047 // OPTIMIZATION: may not need Y
1048 LineXYAtT(fWorkEdge.fPts, tAbove, &fAbove);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001049 double tBelow = t(fTIndex - ~add);
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001050 LineXYAtT(fWorkEdge.fPts, tBelow, &fBelow);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001051 SkASSERT(tAbove != tBelow);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001052 while (fAbove.fY == fBelow.fY) {
1053 if (add < 0) {
1054 add -= 1;
1055 SkASSERT(fTIndex + add >= 0);
1056 tAbove = t(fTIndex + add);
1057 LineXYAtT(fWorkEdge.fPts, tAbove, &fAbove);
1058 } else {
1059 add += 1;
1060 SkASSERT(fTIndex - ~add <= fTs->count() + 1);
1061 tBelow = t(fTIndex - ~add);
1062 LineXYAtT(fWorkEdge.fPts, tBelow, &fBelow);
1063 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001064 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001065 #if COMPARE_DOUBLE
1066 LineXYAtT(fWorkEdge.fPts, tAbove, &fDAbove);
1067 LineXYAtT(fWorkEdge.fPts, tBelow, &fDBelow);
1068 #endif
1069 #if DEBUG_ABOVE_BELOW
1070 fTAbove = tAbove;
1071 fTBelow = tBelow;
1072 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001073 break;
1074 }
1075 default:
1076 // FIXME: add support for all curve types
1077 SkASSERT(0);
1078 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001079 }
1080
caryclark@google.com752b60e2012-03-22 21:11:17 +00001081 bool done(SkScalar bottom) const {
1082 return fDone || fYBottom >= bottom;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001083 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001084
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001085 void fixBelow() {
1086 if (fFixBelow) {
1087 LineXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1088 fFixBelow = false;
1089 }
1090 }
1091
caryclark@google.com6680fb12012-02-07 22:10:51 +00001092 void init(const InEdge* edge) {
1093 fWorkEdge.init(edge);
1094 initT();
caryclark@google.com4917f172012-03-05 22:01:21 +00001095 fBelow.fY = SK_ScalarMin;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001096 fDone = false;
1097 fYBottom = SK_ScalarMin;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001098 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001099
caryclark@google.com6680fb12012-02-07 22:10:51 +00001100 void initT() {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001101 const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
1102 SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
1103 const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
1104 fTs = &interceptPtr->fTs;
1105 // the above is conceptually the same as
1106 // fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
1107 // but templated arrays don't allow returning a pointer to the end() element
caryclark@google.com6680fb12012-02-07 22:10:51 +00001108 fTIndex = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +00001109 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001110
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001111 // OPTIMIZATION: record if two edges are coincident when the are intersected
1112 // It's unclear how to do this -- seems more complicated than recording the
1113 // t values, since the same t values could exist intersecting non-coincident
1114 // edges.
1115 bool isCoincidentWith(const ActiveEdge* edge, SkScalar y) const {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001116
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001117 if (!fAbove.equalsWithinTolerance(edge->fAbove, MIN_PT_DELTA)
1118 || !fBelow.equalsWithinTolerance(edge->fBelow, MIN_PT_DELTA)) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001119 return false;
1120 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001121 uint8_t verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
1122 uint8_t edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb()
1123 : edge->fWorkEdge.verb();
1124 if (verb != edgeVerb) {
1125 return false;
1126 }
1127 switch (verb) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001128 case SkPath::kLine_Verb: {
caryclark@google.com4917f172012-03-05 22:01:21 +00001129 return true;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001130 }
1131 default:
1132 // FIXME: add support for all curve types
1133 SkASSERT(0);
1134 }
1135 return false;
1136 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001137
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001138 // The shortest close call edge should be moved into a position where
1139 // it contributes if the winding is transitioning to or from zero.
1140 bool swapClose(const ActiveEdge* next, int prev, int wind, int mask) const {
1141 if ((prev & mask) == 0 || (wind & mask) == 0) {
1142 return next->fBelow.fY < fBelow.fY;
1143 }
1144 int nextWinding = wind + next->fWorkEdge.winding();
1145 if ((nextWinding & mask) == 0) {
1146 return fBelow.fY < next->fBelow.fY;
1147 }
1148 return false;
1149 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001150
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001151 bool swapCoincident(const ActiveEdge* edge, SkScalar bottom) const {
1152 if (fBelow.fY >= bottom || fDone || edge->fDone) {
1153 return false;
1154 }
1155 ActiveEdge thisWork = *this;
1156 ActiveEdge edgeWork = *edge;
1157 while ((thisWork.advanceT() || thisWork.advance())
1158 && (edgeWork.advanceT() || edgeWork.advance())) {
1159 thisWork.calcLeft();
1160 edgeWork.calcLeft();
1161 if (thisWork < edgeWork) {
1162 return false;
1163 }
1164 if (edgeWork < thisWork) {
1165 return true;
1166 }
1167 }
1168 return false;
1169 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001170
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001171 bool tooCloseToCall(const ActiveEdge* edge) const {
1172 int ulps;
1173 // FIXME: don't compare points for equality
1174 // OPTIMIZATION: refactor to make one call to UlpsDiff
1175 if (edge->fAbove.fY - fAbove.fY > fBelow.fY - edge->fAbove.fY
1176 && fBelow.fY < edge->fBelow.fY
1177 || fAbove.fY - edge->fAbove.fY < edge->fBelow.fY - fAbove.fY
1178 && edge->fBelow.fY < fBelow.fY) {
1179 const SkPoint& check = edge->fBelow.fY <= fBelow.fY
1180 && fBelow != edge->fBelow ? edge->fBelow :
1181 edge->fAbove;
1182 ulps = UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
1183 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX));
1184 } else {
1185 const SkPoint& check = fBelow.fY <= edge->fBelow.fY
1186 && fBelow != edge->fBelow ? fBelow : fAbove;
1187 ulps = UlpsDiff((edge->fBelow.fY - edge->fAbove.fY)
1188 * (check.fX - edge->fAbove.fX),
1189 (check.fY - edge->fAbove.fY)
1190 * (edge->fBelow.fX - edge->fAbove.fX));
1191 }
1192 return ulps >= 0 && ulps <= 32;
1193 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001194
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001195 double nextT() const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001196 SkASSERT(fTIndex <= fTs->count());
1197 return t(fTIndex + 1);
1198 }
caryclark@google.com6680fb12012-02-07 22:10:51 +00001199
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001200 double t() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001201 if (fTIndex == 0) {
1202 return 0;
1203 }
1204 if (fTIndex > fTs->count()) {
1205 return 1;
1206 }
1207 return (*fTs)[fTIndex - 1];
1208 }
1209
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001210 double t(int tIndex) const {
1211 if (tIndex == 0) {
1212 return 0;
1213 }
1214 if (tIndex > fTs->count()) {
1215 return 1;
1216 }
1217 return (*fTs)[tIndex - 1];
1218 }
1219
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001220 // FIXME: debugging only
caryclark@google.com752b60e2012-03-22 21:11:17 +00001221 int ID() const {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001222 return fWorkEdge.fEdge->fID;
1223 }
1224
1225public:
caryclark@google.com6680fb12012-02-07 22:10:51 +00001226 WorkEdge fWorkEdge;
1227 const SkTDArray<double>* fTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001228 SkPoint fAbove;
1229 SkPoint fBelow;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001230#if COMPARE_DOUBLE
1231 _Point fDAbove;
1232 _Point fDBelow;
1233#endif
1234#if DEBUG_ABOVE_BELOW
1235 double fTAbove;
1236 double fTBelow;
1237#endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001238 SkScalar fYBottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001239 int fCoincident;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001240 int fTIndex;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001241 bool fSkip; // OPTIMIZATION: use bitfields?
1242 bool fCloseCall;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001243 bool fDone;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001244 bool fFixBelow;
caryclark@google.comc6825902012-02-03 22:07:47 +00001245};
1246
caryclark@google.com6680fb12012-02-07 22:10:51 +00001247static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001248 size_t count = activeEdges.count();
1249 for (size_t index = 0; index < count; ++index) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001250 if (edge == activeEdges[index].fWorkEdge.fEdge) {
1251 return;
1252 }
1253 }
1254 ActiveEdge* active = activeEdges.append();
1255 active->init(edge);
1256}
1257
caryclark@google.com4917f172012-03-05 22:01:21 +00001258// Find any intersections in the range of active edges. A pair of edges, on
1259// either side of another edge, may change the winding contribution for part of
1260// the edge.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001261// Keep horizontal edges just for
caryclark@google.com4917f172012-03-05 22:01:21 +00001262// the purpose of computing when edges change their winding contribution, since
1263// this is essentially computing the horizontal intersection.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001264static void addBottomT(InEdge** currentPtr, InEdge** lastPtr,
1265 HorizontalEdge** horizontal) {
1266 InEdge** testPtr = currentPtr - 1;
1267 HorizontalEdge* horzEdge = *horizontal;
1268 SkScalar left = horzEdge->fLeft;
1269 SkScalar bottom = horzEdge->fY;
1270 while (++testPtr != lastPtr) {
1271 InEdge* test = *testPtr;
1272 if (test->fBounds.fBottom <= bottom || test->fBounds.fRight <= left) {
1273 continue;
1274 }
1275 WorkEdge wt;
1276 wt.init(test);
1277 do {
1278 HorizontalEdge** sorted = horizontal;
1279 horzEdge = *sorted;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001280 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001281 if (wt.verb() == SkPath::kLine_Verb) {
1282 double wtTs[2];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001283 int pts = LineIntersect(wt.fPts, horzEdge->fLeft,
1284 horzEdge->fRight, horzEdge->fY, wtTs);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001285 if (pts) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001286#if DEBUG_ADD_BOTTOM_TS
1287 SkDebugf("%s y=%g wtTs[0]=%g (%g,%g, %g,%g)\n", __FUNCTION__,
1288 horzEdge->fY, wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
1289 wt.fPts[1].fX, wt.fPts[1].fY);
1290 if (pts == 2) {
1291 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1292 }
1293#endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001294 test->add(wtTs, pts, wt.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001295 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001296 } else {
1297 // FIXME: add all curve types
1298 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001299 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001300 horzEdge = *++sorted;
1301 } while (horzEdge->fY == bottom
1302 && horzEdge->fLeft <= test->fBounds.fRight);
1303 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001304 }
1305}
1306
1307static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001308 InEdge** testPtr = currentPtr - 1;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001309 // FIXME: lastPtr should be past the point of interest, so
1310 // test below should be lastPtr - 2
1311 // that breaks testSimplifyTriangle22, so further investigation is needed
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001312 while (++testPtr != lastPtr - 1) {
1313 InEdge* test = *testPtr;
1314 InEdge** nextPtr = testPtr;
1315 do {
1316 InEdge* next = *++nextPtr;
1317 if (test->cached(next)) {
1318 continue;
1319 }
1320 if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
1321 continue;
1322 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001323 WorkEdge wt, wn;
1324 wt.init(test);
1325 wn.init(next);
1326 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001327 if (wt.verb() == SkPath::kLine_Verb
1328 && wn.verb() == SkPath::kLine_Verb) {
1329 double wtTs[2], wnTs[2];
1330 int pts = LineIntersect(wt.fPts, wn.fPts, wtTs, wnTs);
1331 if (pts) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001332#if DEBUG_ADD_INTERSECTING_TS
1333 SkPoint wtOutPt, wnOutPt;
1334 LineXYAtT(wt.fPts, wtTs[0], &wtOutPt);
1335 LineXYAtT(wn.fPts, wnTs[0], &wnOutPt);
1336 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
1337 __FUNCTION__,
1338 wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
1339 wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY,
1340 test->fID, next->fID);
1341 if (pts == 2) {
1342 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1343 }
1344 SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
1345 __FUNCTION__,
1346 wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY,
1347 wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY,
1348 test->fID, next->fID);
1349 if (pts == 2) {
1350 SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
1351 }
1352#endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001353 test->add(wtTs, pts, wt.verbIndex());
1354 next->add(wnTs, pts, wn.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001355 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001356 } else {
1357 // FIXME: add all combinations of curve types
1358 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001359 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001360 } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
1361 } while (nextPtr != lastPtr);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001362 }
1363}
1364
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001365static InEdge** advanceEdges(SkTDArray<ActiveEdge>* activeEdges,
caryclark@google.com6008c6562012-02-15 22:01:16 +00001366 InEdge** currentPtr, InEdge** lastPtr, SkScalar y) {
1367 InEdge** testPtr = currentPtr - 1;
1368 while (++testPtr != lastPtr) {
1369 if ((*testPtr)->fBounds.fBottom > y) {
1370 continue;
1371 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001372 if (activeEdges) {
1373 InEdge* test = *testPtr;
1374 ActiveEdge* activePtr = activeEdges->begin() - 1;
1375 ActiveEdge* lastActive = activeEdges->end();
1376 while (++activePtr != lastActive) {
1377 if (activePtr->fWorkEdge.fEdge == test) {
1378 activeEdges->remove(activePtr - activeEdges->begin());
1379 break;
1380 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001381 }
1382 }
1383 if (testPtr == currentPtr) {
1384 ++currentPtr;
1385 }
1386 }
1387 return currentPtr;
1388}
1389
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001390// OPTIMIZE: inline?
1391static HorizontalEdge** advanceHorizontal(HorizontalEdge** edge, SkScalar y) {
1392 while ((*edge)->fY < y) {
1393 ++edge;
1394 }
1395 return edge;
1396}
1397
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001398// compute bottom taking into account any intersected edges
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001399static SkScalar computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
1400 SkScalar y, SkScalar bottom) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001401 ActiveEdge* activePtr = activeEdges.begin() - 1;
1402 ActiveEdge* lastActive = activeEdges.end();
1403 while (++activePtr != lastActive) {
1404 const InEdge* test = activePtr->fWorkEdge.fEdge;
1405 if (!test->fContainsIntercepts) {
1406 continue;
1407 }
1408 WorkEdge wt;
1409 wt.init(test);
1410 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001411 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
caryclark@google.com4917f172012-03-05 22:01:21 +00001412 if (intercepts.fTopIntercepts > 1) {
1413 SkScalar yTop = wt.fPts[0].fY;
1414 if (yTop > y && bottom > yTop) {
1415 bottom = yTop;
1416 }
1417 }
1418 if (intercepts.fBottomIntercepts > 1) {
1419 SkScalar yBottom = wt.fPts[wt.verb()].fY;
1420 if (yBottom > y && bottom > yBottom) {
1421 bottom = yBottom;
1422 }
1423 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001424 const SkTDArray<double>& fTs = intercepts.fTs;
1425 size_t count = fTs.count();
1426 for (size_t index = 0; index < count; ++index) {
1427 if (wt.verb() == SkPath::kLine_Verb) {
1428 SkScalar yIntercept = LineYAtT(wt.fPts, fTs[index]);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001429 if (yIntercept > y && bottom > yIntercept) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001430 bottom = yIntercept;
1431 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001432 } else {
1433 // FIXME: add all curve types
1434 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001435 }
1436 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001437 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001438 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001439 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001440}
1441
1442static SkScalar findBottom(InEdge** currentPtr,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001443 InEdge** edgeListEnd, SkTDArray<ActiveEdge>* activeEdges, SkScalar y,
caryclark@google.com6008c6562012-02-15 22:01:16 +00001444 bool asFill, InEdge**& testPtr) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001445 InEdge* current = *currentPtr;
1446 SkScalar bottom = current->fBounds.fBottom;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001447
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001448 // find the list of edges that cross y
caryclark@google.com6008c6562012-02-15 22:01:16 +00001449 InEdge* test = *testPtr;
1450 while (testPtr != edgeListEnd) {
1451 SkScalar testTop = test->fBounds.fTop;
1452 if (bottom <= testTop) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001453 break;
1454 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001455 SkScalar testBottom = test->fBounds.fBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001456 // OPTIMIZATION: Shortening the bottom is only interesting when filling
1457 // and when the edge is to the left of a longer edge. If it's a framing
1458 // edge, or part of the right, it won't effect the longer edges.
caryclark@google.com6008c6562012-02-15 22:01:16 +00001459 if (testTop > y) {
1460 bottom = testTop;
1461 break;
1462 }
1463 if (y < testBottom) {
1464 if (bottom > testBottom) {
1465 bottom = testBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001466 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001467 if (activeEdges) {
1468 addToActive(*activeEdges, test);
1469 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001470 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001471 test = *++testPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001472 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001473 return bottom;
1474}
1475
1476static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
1477 SkTDArray<InEdge*>& edgeList) {
1478 size_t edgeCount = edges.count();
1479 if (edgeCount == 0) {
1480 return;
1481 }
1482 for (size_t index = 0; index < edgeCount; ++index) {
1483 *edgeList.append() = &edges[index];
1484 }
1485 edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1486 *edgeList.append() = &edgeSentinel;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001487 QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001488}
1489
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001490static void makeHorizontalList(SkTDArray<HorizontalEdge>& edges,
1491 HorizontalEdge& edgeSentinel, SkTDArray<HorizontalEdge*>& edgeList) {
1492 size_t edgeCount = edges.count();
1493 if (edgeCount == 0) {
1494 return;
1495 }
1496 for (size_t index = 0; index < edgeCount; ++index) {
1497 *edgeList.append() = &edges[index];
1498 }
1499 edgeSentinel.fLeft = edgeSentinel.fRight = edgeSentinel.fY = SK_ScalarMax;
1500 *edgeList.append() = &edgeSentinel;
1501 QSort<HorizontalEdge>(edgeList.begin(), edgeList.end() - 1);
1502}
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001503
1504static void skipCoincidence(int lastWinding, int winding, int windingMask,
1505 ActiveEdge* activePtr, ActiveEdge* firstCoincident) {
1506 if (((lastWinding & windingMask) == 0) ^ (winding & windingMask) != 0) {
1507 return;
1508 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001509 // FIXME: ? shouldn't this be if (lastWinding & windingMask) ?
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001510 if (lastWinding) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001511#if DEBUG_ADJUST_COINCIDENT
1512 SkDebugf("%s edge=%d 1 set skip=false\n", __FUNCTION__, activePtr->ID());
1513#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001514 activePtr->fSkip = false;
1515 } else {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001516#if DEBUG_ADJUST_COINCIDENT
1517 SkDebugf("%s edge=%d 2 set skip=false\n", __FUNCTION__, firstCoincident->ID());
1518#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001519 firstCoincident->fSkip = false;
1520 }
1521}
1522
caryclark@google.com6008c6562012-02-15 22:01:16 +00001523static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001524 SkTDArray<ActiveEdge*>& edgeList, SkScalar y) {
1525 const int tab = 3; // FIXME: debugging only
caryclark@google.com6008c6562012-02-15 22:01:16 +00001526 size_t edgeCount = activeEdges.count();
1527 if (edgeCount == 0) {
1528 return;
1529 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001530#if DEBUG_SORT_HORIZONTAL
1531 SkDebugf("%s y=%1.9g\n", __FUNCTION__, y);
1532#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00001533 size_t index;
1534 for (index = 0; index < edgeCount; ++index) {
1535 ActiveEdge& activeEdge = activeEdges[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001536 do {
1537 activeEdge.calcLeft(y);
1538 // skip segments that don't span y
1539 if (activeEdge.fAbove != activeEdge.fBelow) {
1540 break;
1541 }
1542 if (activeEdge.fDone) {
1543#if DEBUG_SORT_HORIZONTAL
1544 SkDebugf("%*s edge=%d done\n", tab, "", activeEdge.ID());
1545#endif
1546 goto nextEdge;
1547 }
1548#if DEBUG_SORT_HORIZONTAL
1549 SkDebugf("%*s edge=%d above==below\n", tab, "", activeEdge.ID());
1550#endif
1551 } while (activeEdge.advanceT() || activeEdge.advance());
1552#if DEBUG_SORT_HORIZONTAL
1553 SkDebugf("%*s edge=%d above=(%1.9g,%1.9g) (%1.9g) below=(%1.9g,%1.9g)"
1554 " (%1.9g)\n", tab, "", activeEdge.ID(),
1555 activeEdge.fAbove.fX, activeEdge.fAbove.fY, activeEdge.fTAbove,
1556 activeEdge.fBelow.fX, activeEdge.fBelow.fY, activeEdge.fTBelow);
1557#endif
1558 activeEdge.fSkip = activeEdge.fCloseCall = activeEdge.fFixBelow = false;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001559 *edgeList.append() = &activeEdge;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001560nextEdge:
1561 ;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001562 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001563 QSort<ActiveEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001564}
1565
1566// remove coincident edges
1567// OPTIMIZE: remove edges? This is tricky because the current logic expects
1568// the winding count to be maintained while skipping coincident edges. In
1569// addition to removing the coincident edges, the remaining edges would need
1570// to have a different winding value, possibly different per intercept span.
1571static SkScalar adjustCoincident(SkTDArray<ActiveEdge*>& edgeList,
1572 int windingMask, SkScalar y, SkScalar bottom, OutEdgeBuilder& outBuilder)
1573{
1574#if DEBUG_ADJUST_COINCIDENT
1575 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
1576#endif
1577 size_t edgeCount = edgeList.count();
1578 if (edgeCount == 0) {
1579 return bottom;
1580 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001581 ActiveEdge* activePtr = edgeList[0];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001582 size_t index;
1583 bool foundCoincident = false;
1584 int firstIndex = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001585 for (index = 1; index < edgeCount; ++index) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001586 ActiveEdge* nextPtr = edgeList[index];
1587 bool closeCall = false;
1588 activePtr->fCoincident = firstIndex;
1589 if (activePtr->isCoincidentWith(nextPtr, y)
1590 || (closeCall = activePtr->tooCloseToCall(nextPtr))) {
1591 activePtr->fSkip = nextPtr->fSkip = foundCoincident = true;
1592 activePtr->fCloseCall = nextPtr->fCloseCall = closeCall;
1593 } else {
1594 firstIndex = index;
1595 }
1596 activePtr = nextPtr;
1597 }
1598 activePtr->fCoincident = firstIndex;
1599 if (!foundCoincident) {
1600 return bottom;
1601 }
1602 int winding = 0;
1603 activePtr = edgeList[0];
1604 for (index = 1; index < edgeCount; ++index) {
1605 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001606 winding += activePtr->fWorkEdge.winding();
caryclark@google.com6008c6562012-02-15 22:01:16 +00001607 ActiveEdge* nextPtr = edgeList[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001608 if (activePtr->fCoincident == nextPtr->fCoincident) {
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001609 // the coincident edges may not have been sorted above -- advance
1610 // the edges and resort if needed
1611 // OPTIMIZE: if sorting is done incrementally as new edges are added
1612 // and not all at once as is done here, fold this test into the
1613 // current less than test.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001614 if (activePtr->fCloseCall ? activePtr->swapClose(nextPtr,
1615 priorWinding, winding, windingMask)
1616 : activePtr->swapCoincident(nextPtr, bottom)) {
caryclark@google.com4917f172012-03-05 22:01:21 +00001617 winding -= activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001618 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
1619 SkTSwap<ActiveEdge*>(activePtr, nextPtr);
caryclark@google.com4917f172012-03-05 22:01:21 +00001620 winding += activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001621 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001622 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001623 activePtr = nextPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001624 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001625 int firstCoincidentWinding = 0;
1626 ActiveEdge* firstCoincident = NULL;
1627 winding = 0;
1628 activePtr = edgeList[0];
1629 for (index = 1; index < edgeCount; ++index) {
1630 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001631 winding += activePtr->fWorkEdge.winding();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001632 ActiveEdge* nextPtr = edgeList[index];
1633 if (activePtr->fCoincident == nextPtr->fCoincident) {
1634 if (!firstCoincident) {
1635 firstCoincident = activePtr;
1636 firstCoincidentWinding = priorWinding;
1637 }
1638 if (activePtr->fCloseCall) {
1639 // If one of the edges has already been added to out as a non
1640 // coincident edge, trim it back to the top of this span
1641 if (outBuilder.trimLine(y, activePtr->ID())) {
1642 activePtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001643 #if DEBUG_ADJUST_COINCIDENT
1644 SkDebugf("%s 1 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
1645 __FUNCTION__, activePtr->ID(), y, activePtr->fYBottom);
1646 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001647 activePtr->fYBottom = y;
1648 }
1649 if (outBuilder.trimLine(y, nextPtr->ID())) {
1650 nextPtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001651 #if DEBUG_ADJUST_COINCIDENT
1652 SkDebugf("%s 2 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
1653 __FUNCTION__, nextPtr->ID(), y, nextPtr->fYBottom);
1654 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001655 nextPtr->fYBottom = y;
1656 }
1657 // add missing t values so edges can be the same length
1658 SkScalar testY = activePtr->fBelow.fY;
1659 nextPtr->addTatYBelow(testY);
1660 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001661 #if DEBUG_ADJUST_COINCIDENT
1662 SkDebugf("%s 3 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
1663 __FUNCTION__, activePtr->ID(), testY, bottom);
1664 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001665 bottom = testY;
1666 }
1667 testY = nextPtr->fBelow.fY;
1668 activePtr->addTatYBelow(testY);
1669 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001670 #if DEBUG_ADJUST_COINCIDENT
1671 SkDebugf("%s 4 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
1672 __FUNCTION__, nextPtr->ID(), testY, bottom);
1673 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001674 bottom = testY;
1675 }
1676 }
1677 } else if (firstCoincident) {
1678 skipCoincidence(firstCoincidentWinding, winding, windingMask,
1679 activePtr, firstCoincident);
1680 firstCoincident = NULL;
1681 }
1682 activePtr = nextPtr;
1683 }
1684 if (firstCoincident) {
1685 winding += activePtr->fWorkEdge.winding();
1686 skipCoincidence(firstCoincidentWinding, winding, windingMask, activePtr,
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001687 firstCoincident);
1688 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001689 // fix up the bottom for close call edges. OPTIMIZATION: maybe this could
1690 // be in the loop above, but moved here since loop above reads fBelow and
1691 // it felt unsafe to write it in that loop
1692 for (index = 0; index < edgeCount; ++index) {
1693 (edgeList[index])->fixBelow();
1694 }
1695 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001696}
1697
1698// stitch edge and t range that satisfies operation
caryclark@google.com6008c6562012-02-15 22:01:16 +00001699static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
caryclark@google.com752b60e2012-03-22 21:11:17 +00001700 SkScalar bottom, int windingMask, bool fill, OutEdgeBuilder& outBuilder) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001701 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001702 ActiveEdge** activeHandle = edgeList.begin() - 1;
1703 ActiveEdge** lastActive = edgeList.end();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001704 const int tab = 7; // FIXME: debugging only
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001705 if (gShowDebugf) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001706 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001707 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001708 while (++activeHandle != lastActive) {
1709 ActiveEdge* activePtr = *activeHandle;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001710 const WorkEdge& wt = activePtr->fWorkEdge;
1711 int lastWinding = winding;
1712 winding += wt.winding();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001713 if (gShowDebugf) {
1714 SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d"
1715#if DEBUG_ABOVE_BELOW
1716 " above=%1.9g below=%1.9g"
1717#endif
1718 "\n",
1719 tab-4, "", activePtr->ID(), lastWinding,
1720 winding, activePtr->fSkip, activePtr->fCloseCall
1721#if DEBUG_ABOVE_BELOW
1722 , activePtr->fTAbove, activePtr->fTBelow
1723#endif
1724 );
1725 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001726 if (activePtr->done(bottom)) {
1727 if (gShowDebugf) {
1728 SkDebugf("%*s fDone=%d || fYBottom=%1.9g >= bottom\n", tab, "",
1729 activePtr->fDone, activePtr->fYBottom);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001730 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001731 continue;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001732 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00001733 int opener = (lastWinding & windingMask) == 0;
1734 bool closer = (winding & windingMask) == 0;
1735 SkASSERT(!opener | !closer);
1736 bool inWinding = opener | closer;
caryclark@google.com4917f172012-03-05 22:01:21 +00001737 SkPoint clippedPts[2];
1738 const SkPoint* clipped = NULL;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001739 #if COMPARE_DOUBLE
1740 _Line dPoints;
1741 _Line clippedDPts;
1742 const _Point* dClipped = NULL;
1743 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001744 uint8_t verb = wt.verb();
1745 bool moreToDo, aboveBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001746 do {
1747 double currentT = activePtr->t();
caryclark@google.com6008c6562012-02-15 22:01:16 +00001748 SkASSERT(currentT < 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001749 const SkPoint* points = wt.fPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001750 double nextT;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001751 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001752 nextT = activePtr->nextT();
1753 if (verb == SkPath::kLine_Verb) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001754 // FIXME: obtuse: want efficient way to say
1755 // !currentT && currentT != 1 || !nextT && nextT != 1
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001756 if (currentT * nextT != 0 || currentT + nextT != 1) {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001757 // OPTIMIZATION: if !inWinding, we only need
1758 // clipped[1].fY
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001759 LineSubDivide(points, currentT, nextT, clippedPts);
1760 clipped = clippedPts;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001761 #if COMPARE_DOUBLE
1762 LineSubDivide(points, currentT, nextT, clippedDPts);
1763 dClipped = clippedDPts;
1764 #endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001765 } else {
1766 clipped = points;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001767 #if COMPARE_DOUBLE
1768 dPoints[0].x = SkScalarToDouble(points[0].fX);
1769 dPoints[0].y = SkScalarToDouble(points[1].fY);
1770 dPoints[1].x = SkScalarToDouble(points[0].fX);
1771 dPoints[1].y = SkScalarToDouble(points[1].fY);
1772 dClipped = dPoints;
1773 #endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001774 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001775 if (inWinding && !activePtr->fSkip && (fill ? clipped[0].fY
1776 != clipped[1].fY : clipped[0] != clipped[1])) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001777 if (gShowDebugf) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001778 SkDebugf("%*s line %1.9g,%1.9g %1.9g,%1.9g edge=%d"
1779 " v=%d t=(%1.9g,%1.9g)\n", tab, "",
caryclark@google.comc17972e2012-02-20 21:33:22 +00001780 clipped[0].fX, clipped[0].fY,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001781 clipped[1].fX, clipped[1].fY,
1782 activePtr->ID(),
1783 activePtr->fWorkEdge.fVerb
1784 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
1785 currentT, nextT);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001786 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001787 outBuilder.addLine(clipped,
1788 activePtr->fWorkEdge.fEdge->fID,
1789 activePtr->fCloseCall);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001790 } else {
1791 if (gShowDebugf) {
1792 SkDebugf("%*s skip %1.9g,%1.9g %1.9g,%1.9g"
1793 " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
1794 clipped[0].fX, clipped[0].fY,
1795 clipped[1].fX, clipped[1].fY,
1796 activePtr->ID(),
1797 activePtr->fWorkEdge.fVerb
1798 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
1799 currentT, nextT);
1800 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001801 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001802 // by advancing fAbove/fBelow, the next call to sortHorizontal
1803 // will use these values if they're still valid instead of
1804 // recomputing
1805 #if COMPARE_DOUBLE
1806 SkASSERT((clipped[1].fY > activePtr->fBelow.fY
1807 && bottom >= activePtr->fBelow.fY)
1808 == (dClipped[1].y > activePtr->fDBelow.y
1809 && bottom >= activePtr->fDBelow.y));
1810 #endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001811 if (clipped[1].fY > activePtr->fBelow.fY
1812 && bottom >= activePtr->fBelow.fY ) {
1813 activePtr->fAbove = activePtr->fBelow;
1814 activePtr->fBelow = clipped[1];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001815 #if COMPARE_DOUBLE
1816 activePtr->fDAbove = activePtr->fDBelow;
1817 activePtr->fDBelow = dClipped[1];
1818 #endif
1819 #if DEBUG_ABOVE_BELOW
1820 activePtr->fTAbove = activePtr->fTBelow;
1821 activePtr->fTBelow = nextT;
1822 #endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001823 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001824 } else {
1825 // FIXME: add all curve types
1826 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001827 }
1828 currentT = nextT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001829 moreToDo = activePtr->advanceT();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001830 activePtr->fYBottom = clipped[verb].fY; // was activePtr->fCloseCall ? bottom :
1831
1832 // clearing the fSkip/fCloseCall bit here means that trailing edges
1833 // fall out of sync, if one edge is long and another is a series of short pieces
1834 // if fSkip/fCloseCall is set, need to recompute coincidence/too-close-to-call
1835 // after advancing
1836 // another approach would be to restrict bottom to smaller part of close call
1837 // maybe this is already happening with coincidence when intersection is computed,
1838 // and needs to be added to the close call computation as well
1839 // this is hard to do because that the bottom is important is not known when
1840 // the lines are intersected; only when the computation for edge sorting is done
1841 // does the need for new bottoms become apparent.
1842 // maybe this is good incentive to scrap the current sort and do an insertion
1843 // sort that can take this into consideration when the x value is computed
1844
1845 // FIXME: initialized in sortHorizontal, cleared here as well so
1846 // that next edge is not skipped -- but should skipped edges ever
1847 // continue? (probably not)
caryclark@google.com752b60e2012-03-22 21:11:17 +00001848 aboveBottom = clipped[verb].fY < bottom;
1849 if (clipped[0].fY != clipped[verb].fY) {
1850 activePtr->fSkip = false;
1851 activePtr->fCloseCall = false;
1852 aboveBottom &= !activePtr->fCloseCall;
1853 } else {
1854 if (activePtr->fSkip || activePtr->fCloseCall) {
1855 SkDebugf("== %1.9g\n", clippedPts[0].fY);
1856 }
1857 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001858 } while (moreToDo & aboveBottom);
1859 } while ((moreToDo || activePtr->advance()) & aboveBottom);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001860 }
1861}
1862
caryclark@google.comc6825902012-02-03 22:07:47 +00001863void simplify(const SkPath& path, bool asFill, SkPath& simple) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001864 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
1865 int windingMask = (path.getFillType() & 1) ? 1 : -1;
1866 simple.reset();
1867 simple.setFillType(SkPath::kEvenOdd_FillType);
caryclark@google.comc6825902012-02-03 22:07:47 +00001868 // turn path into list of edges increasing in y
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001869 // if an edge is a quad or a cubic with a y extrema, note it, but leave it
1870 // unbroken. Once we have a list, sort it, then walk the list (walk edges
1871 // twice that have y extrema's on top) and detect crossings -- look for raw
1872 // bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +00001873 SkTArray<InEdge> edges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001874 SkTDArray<HorizontalEdge> horizontalEdges;
1875 InEdgeBuilder builder(path, asFill, edges, horizontalEdges);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001876 SkTDArray<InEdge*> edgeList;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001877 InEdge edgeSentinel;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001878 makeEdgeList(edges, edgeSentinel, edgeList);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001879 SkTDArray<HorizontalEdge*> horizontalList;
1880 HorizontalEdge horizontalSentinel;
1881 makeHorizontalList(horizontalEdges, horizontalSentinel, horizontalList);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001882 InEdge** currentPtr = edgeList.begin();
caryclark@google.com4917f172012-03-05 22:01:21 +00001883 if (!currentPtr) {
1884 return;
1885 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001886 // find all intersections between edges
1887// beyond looking for horizontal intercepts, we need to know if any active edges
1888// intersect edges below 'bottom', but above the active edge segment.
1889// maybe it makes more sense to compute all intercepts before doing anything
1890// else, since the intercept list is long-lived, at least in the current design.
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001891 SkScalar y = (*currentPtr)->fBounds.fTop;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001892 HorizontalEdge** currentHorizontal = horizontalList.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00001893 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001894 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001895 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001896 NULL, y, asFill, lastPtr);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001897 if (lastPtr > currentPtr) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001898 if (currentHorizontal) {
1899 if ((*currentHorizontal)->fY < SK_ScalarMax) {
1900 addBottomT(currentPtr, lastPtr, currentHorizontal);
1901 }
1902 currentHorizontal = advanceHorizontal(currentHorizontal, bottom);
1903 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00001904 addIntersectingTs(currentPtr, lastPtr);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001905 }
1906 y = bottom;
1907 currentPtr = advanceEdges(NULL, currentPtr, lastPtr, y);
1908 } while (*currentPtr != &edgeSentinel);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001909
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001910#if DEBUG_DUMP
1911 InEdge** debugPtr = edgeList.begin();
1912 do {
1913 (*debugPtr++)->dump();
1914 } while (*debugPtr != &edgeSentinel);
1915#endif
caryclark@google.com752b60e2012-03-22 21:11:17 +00001916
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001917 // walk the sorted edges from top to bottom, computing accumulated winding
1918 SkTDArray<ActiveEdge> activeEdges;
1919 OutEdgeBuilder outBuilder(asFill);
1920 currentPtr = edgeList.begin();
1921 y = (*currentPtr)->fBounds.fTop;
1922 do {
1923 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
1924 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
1925 &activeEdges, y, asFill, lastPtr);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001926#if DEBUG_BOTTOM
1927 SkDebugf("%s findBottom bottom=%1.9g\n", __FUNCTION__, bottom);
1928#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001929 if (lastPtr > currentPtr) {
1930 bottom = computeInterceptBottom(activeEdges, y, bottom);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001931#if DEBUG_BOTTOM
1932 SkDebugf("%s computeInterceptBottom bottom=%1.9g\n", __FUNCTION__, bottom);
1933#endif
caryclark@google.comc17972e2012-02-20 21:33:22 +00001934 SkTDArray<ActiveEdge*> activeEdgeList;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001935 sortHorizontal(activeEdges, activeEdgeList, y);
1936 bottom = adjustCoincident(activeEdgeList, windingMask, y, bottom,
1937 outBuilder);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001938#if DEBUG_BOTTOM
1939 SkDebugf("%s adjustCoincident bottom=%1.9g\n", __FUNCTION__, bottom);
1940#endif
1941 stitchEdge(activeEdgeList, y, bottom, windingMask, asFill, outBuilder);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001942 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001943 y = bottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001944 // OPTIMIZATION: as edges expire, InEdge allocations could be released
1945 currentPtr = advanceEdges(&activeEdges, currentPtr, lastPtr, y);
caryclark@google.comc6825902012-02-03 22:07:47 +00001946 } while (*currentPtr != &edgeSentinel);
caryclark@google.comc6825902012-02-03 22:07:47 +00001947 // assemble output path from string of pts, verbs
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001948 outBuilder.bridge();
1949 outBuilder.assemble(simple);
caryclark@google.comc6825902012-02-03 22:07:47 +00001950}