blob: e33fd946815d9f471d8a6eb6bbfebd09b949f9bd [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
25#define ASSERT_ON_ULPS 0
26#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
32#else
33static bool gShowDebugf = true; // FIXME: remove once debugging is complete
34
35#define DEBUG_DUMP 01
36#define DEBUG_ADD 01
37#define DEBUG_ADD_INTERSECTING_TS 0
38#define DEBUG_ADD_BOTTOM_TS 0
39#define COMPARE_DOUBLE 0
40#define ASSERT_ON_ULPS 0
41#define DEBUG_ABOVE_BELOW 01
42#define DEBUG_ACTIVE_LESS_THAN 0
43#define DEBUG_SORT_HORIZONTAL 01
44#define DEBUG_OUT 01
45#define DEBUG_OUT_LESS_THAN 0
46#define DEBUG_ADJUST_COINCIDENT 1
47#endif
48
49// FIXME: not wild about this -- min delta should be based on size of curve, not t
50// #define MIN_T_DELTA 0.000001
51// not wild about this either -- for SkScalars backed by floats, would like to
52// represent deltas in terms of number of significant matching bits
53#define MIN_PT_DELTA 0.000001
caryclark@google.comc17972e2012-02-20 21:33:22 +000054
caryclark@google.com6680fb12012-02-07 22:10:51 +000055static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
caryclark@google.comc6825902012-02-03 22:07:47 +000056 double aRange[2], double bRange[2]) {
57 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
58 _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
59 return intersect(aLine, bLine, aRange, bRange);
60}
61
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000062static int LineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
63 SkScalar y, double aRange[2]) {
caryclark@google.comc6825902012-02-03 22:07:47 +000064 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000065 return horizontalLineIntersect(aLine, left, right, y, aRange);
caryclark@google.comc6825902012-02-03 22:07:47 +000066}
67
caryclark@google.comcd4421d2012-03-01 19:16:31 +000068static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +000069 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.comcd4421d2012-03-01 19:16:31 +000070 double x, y;
71 xy_at_t(aLine, t, x, y);
72 out->fX = SkDoubleToScalar(x);
73 out->fY = SkDoubleToScalar(y);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +000074}
75
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000076#if COMPARE_DOUBLE
77static void LineXYAtT(const SkPoint a[2], double t, _Point* out) {
78 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
79 xy_at_t(aLine, t, out->x, out->y);
80}
81#endif
82
83#if 0 // unused for now
84static SkScalar LineXAtT(const SkPoint a[2], double t) {
85 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
86 double x;
87 xy_at_t(aLine, t, x, *(double*) 0);
88 return SkDoubleToScalar(x);
89}
90#endif
91
caryclark@google.com6680fb12012-02-07 22:10:51 +000092static SkScalar LineYAtT(const SkPoint a[2], double t) {
93 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
94 double y;
95 xy_at_t(aLine, t, *(double*) 0, y);
96 return SkDoubleToScalar(y);
97}
98
99static void LineSubDivide(const SkPoint a[2], double startT, double endT,
100 SkPoint sub[2]) {
101 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
102 _Line dst;
103 sub_divide(aLine, startT, endT, dst);
104 sub[0].fX = SkDoubleToScalar(dst[0].x);
105 sub[0].fY = SkDoubleToScalar(dst[0].y);
106 sub[1].fX = SkDoubleToScalar(dst[1].x);
107 sub[1].fY = SkDoubleToScalar(dst[1].y);
108}
109
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000110#if COMPARE_DOUBLE
111static void LineSubDivide(const SkPoint a[2], double startT, double endT,
112 _Line& dst) {
113 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
114 sub_divide(aLine, startT, endT, dst);
115}
116#endif
caryclark@google.com6680fb12012-02-07 22:10:51 +0000117
caryclark@google.comc6825902012-02-03 22:07:47 +0000118// functions
119void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray);
120void simplify(const SkPath& path, bool asFill, SkPath& simple);
121/*
122list of edges
123bounds for edge
124sort
125active T
126
127if a contour's bounds is outside of the active area, no need to create edges
128*/
129
130/* given one or more paths,
131 find the bounds of each contour, select the active contours
132 for each active contour, compute a set of edges
133 each edge corresponds to one or more lines and curves
134 leave edges unbroken as long as possible
135 when breaking edges, compute the t at the break but leave the control points alone
136
137 */
138
139void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray) {
140 SkPath::Iter iter(path, false);
141 SkPoint pts[4];
142 SkPath::Verb verb;
143 SkRect bounds;
144 bounds.setEmpty();
145 int count = 0;
146 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
147 switch (verb) {
148 case SkPath::kMove_Verb:
149 if (!bounds.isEmpty()) {
150 *boundsArray.append() = bounds;
151 }
152 bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
153 count = 0;
154 break;
155 case SkPath::kLine_Verb:
156 count = 1;
157 break;
158 case SkPath::kQuad_Verb:
159 count = 2;
160 break;
161 case SkPath::kCubic_Verb:
162 count = 3;
163 break;
164 case SkPath::kClose_Verb:
165 count = 0;
166 break;
167 default:
168 SkDEBUGFAIL("bad verb");
169 return;
170 }
171 for (int i = 1; i <= count; ++i) {
172 bounds.growToInclude(pts[i].fX, pts[i].fY);
173 }
174 }
175}
176
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000177static bool extendLine(const SkPoint line[2], const SkPoint& add) {
178 // FIXME: allow this to extend lines that have slopes that are nearly equal
179 SkScalar dx1 = line[1].fX - line[0].fX;
180 SkScalar dy1 = line[1].fY - line[0].fY;
181 SkScalar dx2 = add.fX - line[0].fX;
182 SkScalar dy2 = add.fY - line[0].fY;
183 return dx1 * dy2 == dx2 * dy1;
184}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000185
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000186// OPTIMIZATION: this should point to a list of input data rather than duplicating
187// the line data here. This would reduce the need to assemble the results.
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000188struct OutEdge {
189 bool operator<(const OutEdge& rh) const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000190 const SkPoint& first = fPts[0];
191 const SkPoint& rhFirst = rh.fPts[0];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000192 return first.fY == rhFirst.fY
193 ? first.fX < rhFirst.fX
194 : first.fY < rhFirst.fY;
195 }
196
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000197 SkPoint fPts[4];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000198 int fID; // id of edge generating data
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000199 uint8_t fVerb; // FIXME: not read from everywhere
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000200 bool fCloseCall; // edge is trimmable if not originally coincident
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000201};
202
caryclark@google.com6680fb12012-02-07 22:10:51 +0000203class OutEdgeBuilder {
204public:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000205 OutEdgeBuilder(bool fill)
206 : fFill(fill) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000207 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000208
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000209 void addLine(const SkPoint line[2], int id, bool closeCall) {
caryclark@google.comc17972e2012-02-20 21:33:22 +0000210 OutEdge& newEdge = fEdges.push_back();
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000211 newEdge.fPts[0] = line[0];
212 newEdge.fPts[1] = line[1];
213 newEdge.fVerb = SkPath::kLine_Verb;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000214 newEdge.fID = id;
215 newEdge.fCloseCall = closeCall;
216 }
217
218 bool trimLine(SkScalar y, int id) {
219 size_t count = fEdges.count();
220 while (count-- != 0) {
221 OutEdge& edge = fEdges[count];
222 if (edge.fID != id) {
223 continue;
224 }
225 if (edge.fCloseCall) {
226 return false;
227 }
228 SkASSERT(edge.fPts[0].fY <= y);
229 if (edge.fPts[1].fY <= y) {
230 continue;
231 }
232 edge.fPts[1].fX = edge.fPts[0].fX + (y - edge.fPts[0].fY)
233 * (edge.fPts[1].fX - edge.fPts[0].fX)
234 / (edge.fPts[1].fY - edge.fPts[0].fY);
235 edge.fPts[1].fY = y;
236 if (gShowDebugf) {
237 SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id,
238 edge.fPts[1].fX, y);
239 }
240 return true;
241 }
242 return false;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000243 }
244
245 void assemble(SkPath& simple) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000246 size_t listCount = fEdges.count();
247 if (listCount == 0) {
248 return;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000249 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000250 do {
251 size_t listIndex = 0;
252 int advance = 1;
253 while (listIndex < listCount && fTops[listIndex] == 0) {
254 ++listIndex;
255 }
256 if (listIndex >= listCount) {
257 break;
258 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000259 int closeEdgeIndex = -listIndex - 1;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000260 SkPoint firstPt, lastLine[2];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000261 bool doMove = true;
262 int edgeIndex;
263 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000264 SkPoint* ptArray = fEdges[listIndex].fPts;
265 uint8_t verb = fEdges[listIndex].fVerb;
266 SkPoint* start, * end;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000267 if (advance < 0) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000268 start = &ptArray[verb];
269 end = &ptArray[0];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000270 } else {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000271 start = &ptArray[0];
272 end = &ptArray[verb];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000273 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000274 switch (verb) {
275 case SkPath::kLine_Verb:
276 bool gap;
277 if (doMove) {
278 firstPt = *start;
279 simple.moveTo(start->fX, start->fY);
280 if (gShowDebugf) {
281 SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
282 start->fX, start->fY);
283 }
284 lastLine[0] = *start;
285 lastLine[1] = *end;
286 doMove = false;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000287 break;
288 }
289 gap = lastLine[1] != *start;
290 if (gap) {
291 SkASSERT(fFill && lastLine[1].fY == start->fY);
292 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)) {
299 SkASSERT(lastLine[1] == *start ||
300 (fFill && lastLine[1].fY == start->fY));
301 simple.lineTo(start->fX, start->fY);
302 if (gShowDebugf) {
303 SkDebugf("%s lineTo (%g,%g)\n", __FUNCTION__,
304 start->fX, start->fY);
305 }
306 lastLine[0] = *start;
307 }
308 lastLine[1] = *end;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000309 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000310 default:
311 // FIXME: add other curve types
312 ;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000313 }
314 if (advance < 0) {
315 edgeIndex = fTops[listIndex];
316 fTops[listIndex] = 0;
317 } else {
318 edgeIndex = fBottoms[listIndex];
319 fBottoms[listIndex] = 0;
320 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000321 if (edgeIndex) {
322 listIndex = abs(edgeIndex) - 1;
323 if (edgeIndex < 0) {
324 fTops[listIndex] = 0;
325 } else {
326 fBottoms[listIndex] = 0;
327 }
328 }
329 if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
330 if (lastLine[1] != firstPt) {
331 simple.lineTo(lastLine[1].fX, lastLine[1].fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000332 if (gShowDebugf) {
333 SkDebugf("%s lineTo last (%g, %g)\n", __FUNCTION__,
334 lastLine[1].fX, lastLine[1].fY);
335 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000336 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000337 simple.lineTo(firstPt.fX, firstPt.fY);
338 simple.close();
339 if (gShowDebugf) {
caryclark@google.com4917f172012-03-05 22:01:21 +0000340 SkDebugf("%s close (%g, %g)\n", __FUNCTION__,
341 firstPt.fX, firstPt.fY);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000342 }
343 break;
344 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000345 // if this and next edge go different directions
346 if (advance > 0 ^ edgeIndex < 0) {
347 advance = -advance;
348 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000349 } while (edgeIndex);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000350 } while (true);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000351 }
352
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000353 // sort points by y, then x
354 // if x/y is identical, sort bottoms before tops
355 // if identical and both tops/bottoms, sort by angle
356 static bool lessThan(SkTArray<OutEdge>& edges, const int one,
357 const int two) {
358 const OutEdge& oneEdge = edges[abs(one) - 1];
359 int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
360 const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
361 const OutEdge& twoEdge = edges[abs(two) - 1];
362 int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
363 const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
364 if (startPt1.fY != startPt2.fY) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000365 #if DEBUG_OUT_LESS_THAN
366 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
367 one, two, startPt1.fY, startPt2.fY,
368 startPt1.fY < startPt2.fY ? "true" : "false");
369 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000370 return startPt1.fY < startPt2.fY;
371 }
372 if (startPt1.fX != startPt2.fX) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000373 #if DEBUG_OUT_LESS_THAN
374 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
375 one, two, startPt1.fX, startPt2.fX,
376 startPt1.fX < startPt2.fX ? "true" : "false");
377 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000378 return startPt1.fX < startPt2.fX;
379 }
380 const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
381 const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
382 SkScalar dy1 = startPt1.fY - endPt1.fY;
383 SkScalar dy2 = startPt2.fY - endPt2.fY;
384 SkScalar dy1y2 = dy1 * dy2;
385 if (dy1y2 < 0) { // different signs
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000386 #if DEBUG_OUT_LESS_THAN
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000387 SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
388 dy1 > 0 ? "true" : "false");
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000389 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000390 return dy1 > 0; // one < two if one goes up and two goes down
391 }
392 if (dy1y2 == 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000393 #if DEBUG_OUT_LESS_THAN
394 SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
395 one, two, endPt1.fX < endPt2.fX ? "true" : "false");
396 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000397 return endPt1.fX < endPt2.fX;
398 }
399 SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
400 SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000401 #if DEBUG_OUT_LESS_THAN
402 SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
403 one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
404 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000405 return dy2 > 0 ^ dx1y2 < dx2y1;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000406 }
407
caryclark@google.com6008c6562012-02-15 22:01:16 +0000408 // Sort the indices of paired points and then create more indices so
409 // assemble() can find the next edge and connect the top or bottom
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000410 void bridge() {
411 size_t index;
412 size_t count = fEdges.count();
413 if (!count) {
414 return;
415 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000416 SkASSERT(!fFill || count > 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000417 fTops.setCount(count);
418 sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
419 fBottoms.setCount(count);
420 sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000421 SkTDArray<int> order;
422 for (index = 1; index <= count; ++index) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000423 *order.append() = -index;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000424 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000425 for (index = 1; index <= count; ++index) {
426 *order.append() = index;
427 }
428 QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000429 int* lastPtr = order.end() - 1;
430 int* leftPtr = order.begin();
431 while (leftPtr < lastPtr) {
432 int leftIndex = *leftPtr;
433 int leftOutIndex = abs(leftIndex) - 1;
434 const OutEdge& left = fEdges[leftOutIndex];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000435 int* rightPtr = leftPtr + 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000436 int rightIndex = *rightPtr;
437 int rightOutIndex = abs(rightIndex) - 1;
438 const OutEdge& right = fEdges[rightOutIndex];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000439 bool pairUp = fFill;
440 if (!pairUp) {
441 const SkPoint& leftMatch =
442 left.fPts[leftIndex < 0 ? 0 : left.fVerb];
443 const SkPoint& rightMatch =
444 right.fPts[rightIndex < 0 ? 0 : right.fVerb];
445 pairUp = leftMatch == rightMatch;
446 } else {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000447 #if DEBUG_OUT
448 if (left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY
449 != right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) {
450 *fMismatches.append() = leftIndex;
451 if (rightPtr == lastPtr) {
452 *fMismatches.append() = rightIndex;
453 }
454 pairUp = false;
455 }
456 #else
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000457 SkASSERT(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY
458 == right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000459 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000460 }
461 if (pairUp) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000462 if (leftIndex < 0) {
463 fTops[leftOutIndex] = rightIndex;
464 } else {
465 fBottoms[leftOutIndex] = rightIndex;
466 }
467 if (rightIndex < 0) {
468 fTops[rightOutIndex] = leftIndex;
469 } else {
470 fBottoms[rightOutIndex] = leftIndex;
471 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000472 ++rightPtr;
473 }
474 leftPtr = rightPtr;
475 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000476#if DEBUG_OUT
477 int* mismatch = fMismatches.begin();
478 while (mismatch != fMismatches.end()) {
479 int leftIndex = *mismatch++;
480 int leftOutIndex = abs(leftIndex) - 1;
481 const OutEdge& left = fEdges[leftOutIndex];
482 const SkPoint& leftPt = left.fPts[leftIndex < 0 ? 0 : left.fVerb];
483 SkDebugf("%s left=%d %s (%1.9g,%1.9g)\n",
484 __FUNCTION__, left.fID, leftIndex < 0 ? "top" : "bot",
485 leftPt.fX, leftPt.fY);
486 }
487 SkASSERT(fMismatches.count() == 0);
488#endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000489 }
490
caryclark@google.com6008c6562012-02-15 22:01:16 +0000491protected:
caryclark@google.com6680fb12012-02-07 22:10:51 +0000492 SkTArray<OutEdge> fEdges;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000493 SkTDArray<int> fTops;
494 SkTDArray<int> fBottoms;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000495 bool fFill;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000496#if DEBUG_OUT
497 SkTDArray<int> fMismatches;
498#endif
caryclark@google.com6680fb12012-02-07 22:10:51 +0000499};
500
caryclark@google.comc6825902012-02-03 22:07:47 +0000501// Bounds, unlike Rect, does not consider a vertical line to be empty.
502struct Bounds : public SkRect {
503 static bool Intersects(const Bounds& a, const Bounds& b) {
504 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
505 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
506 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000507
508 bool isEmpty() {
509 return fLeft > fRight || fTop > fBottom
510 || fLeft == fRight && fTop == fBottom
511 || isnan(fLeft) || isnan(fRight)
512 || isnan(fTop) || isnan(fBottom);
513 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000514};
515
caryclark@google.com4917f172012-03-05 22:01:21 +0000516class Intercepts {
517public:
518 Intercepts()
519 : fTopIntercepts(0)
520 , fBottomIntercepts(0) {
521 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000522
523#if DEBUG_DUMP
524 // FIXME: pass current verb as parameter
525 void dump(const SkPoint* pts) {
526 const char className[] = "Intercepts";
527 const int tab = 8;
528 for (int i = 0; i < fTs.count(); ++i) {
529 SkPoint out;
530 LineXYAtT(pts, fTs[i], &out);
531 SkDebugf("%*s.fTs[%d]=%g (%g,%g)\n", tab + sizeof(className),
532 className, i, fTs[i], out.fX, out.fY);
533 }
534 SkDebugf("%*s.fTopIntercepts=%d\n", tab + sizeof(className),
535 className, fTopIntercepts);
536 SkDebugf("%*s.fBottomIntercepts=%d\n", tab + sizeof(className),
537 className, fBottomIntercepts);
538 }
539#endif
540
caryclark@google.comc6825902012-02-03 22:07:47 +0000541 SkTDArray<double> fTs;
caryclark@google.com4917f172012-03-05 22:01:21 +0000542 int fTopIntercepts;
543 int fBottomIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000544};
545
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000546struct HorizontalEdge {
547 bool operator<(const HorizontalEdge& rh) const {
548 return fY == rh.fY ? fLeft == rh.fLeft ? fRight < rh.fRight
549 : fLeft < rh.fLeft : fY < rh.fY;
550 }
551
552#if DEBUG_DUMP
553 void dump() {
554 const char className[] = "HorizontalEdge";
555 const int tab = 4;
556 SkDebugf("%*s.fLeft=%g\n", tab + sizeof(className), className, fLeft);
557 SkDebugf("%*s.fRight=%g\n", tab + sizeof(className), className, fRight);
558 SkDebugf("%*s.fY=%g\n", tab + sizeof(className), className, fY);
559 }
560#endif
561
562 SkScalar fLeft;
563 SkScalar fRight;
564 SkScalar fY;
565};
566
caryclark@google.com6680fb12012-02-07 22:10:51 +0000567struct InEdge {
568 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000569 return fBounds.fTop == rh.fBounds.fTop
570 ? fBounds.fLeft < rh.fBounds.fLeft
571 : fBounds.fTop < rh.fBounds.fTop;
572 }
573
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000574 // Avoid collapsing t values that are close to the same since
575 // we walk ts to describe consecutive intersections. Since a pair of ts can
576 // be nearly equal, any problems caused by this should be taken care
577 // of later.
578 int add(double* ts, size_t count, ptrdiff_t verbIndex) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000579 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
caryclark@google.com6008c6562012-02-15 22:01:16 +0000580 bool foundIntercept = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000581 int insertedAt = -1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000582 Intercepts& intercepts = fIntercepts[verbIndex];
caryclark@google.comc6825902012-02-03 22:07:47 +0000583 for (size_t index = 0; index < count; ++index) {
584 double t = ts[index];
caryclark@google.com4917f172012-03-05 22:01:21 +0000585 if (t <= 0) {
586 fContainsIntercepts |= ++intercepts.fTopIntercepts > 1;
587 continue;
588 }
589 if (t >= 1) {
590 fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000591 continue;
592 }
593 foundIntercept = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000594 size_t tCount = intercepts.fTs.count();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000595 double delta;
caryclark@google.comc6825902012-02-03 22:07:47 +0000596 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
597 if (t <= intercepts.fTs[idx2]) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000598 // FIXME: ? if (t < intercepts.fTs[idx2]) // failed
599 delta = intercepts.fTs[idx2] - t;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000600 if (delta > 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000601 insertedAt = idx2;
caryclark@google.comc6825902012-02-03 22:07:47 +0000602 *intercepts.fTs.insert(idx2) = t;
caryclark@google.comc6825902012-02-03 22:07:47 +0000603 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000604 goto nextPt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000605 }
606 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000607 if (tCount == 0 || (delta = t - intercepts.fTs[tCount - 1]) > 0) {
608 insertedAt = tCount;
caryclark@google.comc6825902012-02-03 22:07:47 +0000609 *intercepts.fTs.append() = t;
610 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000611 nextPt:
612 ;
caryclark@google.comc6825902012-02-03 22:07:47 +0000613 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000614 fContainsIntercepts |= foundIntercept;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000615 return insertedAt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000616 }
617
caryclark@google.com6680fb12012-02-07 22:10:51 +0000618 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000619 // FIXME: in the pathological case where there is a ton of edges, binary search?
620 size_t count = fCached.count();
621 for (size_t index = 0; index < count; ++index) {
622 if (edge == fCached[index]) {
623 return true;
624 }
625 if (edge < fCached[index]) {
626 *fCached.insert(index) = edge;
627 return false;
628 }
629 }
630 *fCached.append() = edge;
631 return false;
632 }
633
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000634 void complete(signed char winding, int id) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000635 SkPoint* ptPtr = fPts.begin();
636 SkPoint* ptLast = fPts.end();
637 if (ptPtr == ptLast) {
638 SkDebugf("empty edge\n");
639 SkASSERT(0);
640 // FIXME: delete empty edge?
641 return;
642 }
643 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
644 ++ptPtr;
645 while (ptPtr != ptLast) {
646 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
647 ++ptPtr;
648 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000649 fIntercepts.push_back_n(fVerbs.count());
caryclark@google.com6680fb12012-02-07 22:10:51 +0000650 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
651 size_t index;
652 size_t last = fPts.count() - 1;
653 for (index = 0; index < last; ++index, --last) {
654 SkTSwap<SkPoint>(fPts[index], fPts[last]);
655 }
656 last = fVerbs.count() - 1;
657 for (index = 0; index < last; ++index, --last) {
658 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
659 }
660 }
661 fContainsIntercepts = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000662 fID = id;
caryclark@google.comc6825902012-02-03 22:07:47 +0000663 }
664
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000665#if DEBUG_DUMP
666 void dump() {
667 int i;
668 const char className[] = "InEdge";
669 const int tab = 4;
670 SkDebugf("InEdge %p (edge=%d)\n", this, fID);
671 for (i = 0; i < fCached.count(); ++i) {
672 SkDebugf("%*s.fCached[%d]=0x%08x\n", tab + sizeof(className),
673 className, i, fCached[i]);
674 }
675 uint8_t* verbs = fVerbs.begin();
676 SkPoint* pts = fPts.begin();
677 for (i = 0; i < fIntercepts.count(); ++i) {
678 SkDebugf("%*s.fIntercepts[%d]:\n", tab + sizeof(className),
679 className, i);
680 // FIXME: take current verb into consideration
681 fIntercepts[i].dump(pts);
682 pts += *verbs++;
683 }
684 for (i = 0; i < fPts.count(); ++i) {
685 SkDebugf("%*s.fPts[%d]=(%g,%g)\n", tab + sizeof(className),
686 className, i, fPts[i].fX, fPts[i].fY);
687 }
688 for (i = 0; i < fVerbs.count(); ++i) {
689 SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className),
690 className, i, fVerbs[i]);
691 }
692 SkDebugf("%*s.fBounds=(%g. %g, %g, %g)\n", tab + sizeof(className),
693 className, fBounds.fLeft, fBounds.fTop,
694 fBounds.fRight, fBounds.fBottom);
695 SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className,
696 fWinding);
697 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
698 className, fContainsIntercepts);
699 }
700#endif
701
caryclark@google.comc6825902012-02-03 22:07:47 +0000702 // temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +0000703 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +0000704 SkTArray<Intercepts> fIntercepts; // one per verb
caryclark@google.com4917f172012-03-05 22:01:21 +0000705
caryclark@google.comc6825902012-02-03 22:07:47 +0000706 // persistent data
707 SkTDArray<SkPoint> fPts;
708 SkTDArray<uint8_t> fVerbs;
709 Bounds fBounds;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000710 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +0000711 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000712 bool fContainsIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000713};
714
caryclark@google.com6680fb12012-02-07 22:10:51 +0000715class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +0000716public:
717
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000718InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges,
719 SkTDArray<HorizontalEdge>& horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000720 : fPath(path)
721 , fCurrentEdge(NULL)
722 , fEdges(edges)
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000723 , fID(0)
724 , fHorizontalEdges(horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000725 , fIgnoreHorizontal(ignoreHorizontal)
726{
727 walk();
728}
729
730protected:
731
732void addEdge() {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000733 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +0000734 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
735 fPtOffset = 1;
736 *fCurrentEdge->fVerbs.append() = fVerb;
737}
738
caryclark@google.com6008c6562012-02-15 22:01:16 +0000739bool complete() {
740 if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000741 fCurrentEdge->complete(fWinding, ++fID);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000742 fCurrentEdge = NULL;
743 return true;
744 }
745 return false;
746}
747
caryclark@google.comc6825902012-02-03 22:07:47 +0000748int direction(int count) {
749 fPtCount = count;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000750 if (fIgnoreHorizontal && isHorizontal()) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000751 return 0;
752 }
753 int last = count - 1;
754 return fPts[0].fY == fPts[last].fY
caryclark@google.com6680fb12012-02-07 22:10:51 +0000755 ? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX
756 ? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +0000757}
758
759bool isHorizontal() {
760 SkScalar y = fPts[0].fY;
761 for (int i = 1; i < fPtCount; ++i) {
762 if (fPts[i].fY != y) {
763 return false;
764 }
765 }
766 return true;
767}
768
769void startEdge() {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000770 if (!fCurrentEdge) {
771 fCurrentEdge = fEdges.push_back_n(1);
772 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000773 fWinding = 0;
774 fPtOffset = 0;
775}
776
777void walk() {
778 SkPath::Iter iter(fPath, true);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000779 int winding = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +0000780 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
781 switch (fVerb) {
782 case SkPath::kMove_Verb:
caryclark@google.comc6825902012-02-03 22:07:47 +0000783 startEdge();
784 continue;
785 case SkPath::kLine_Verb:
786 winding = direction(2);
787 break;
788 case SkPath::kQuad_Verb:
789 winding = direction(3);
790 break;
791 case SkPath::kCubic_Verb:
792 winding = direction(4);
793 break;
794 case SkPath::kClose_Verb:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000795 SkASSERT(fCurrentEdge);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000796 complete();
caryclark@google.comc6825902012-02-03 22:07:47 +0000797 continue;
798 default:
799 SkDEBUGFAIL("bad verb");
800 return;
801 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000802 if (winding == 0) {
803 HorizontalEdge* horizontalEdge = fHorizontalEdges.append();
804 // FIXME: for degenerate quads and cubics, compute x extremes
805 horizontalEdge->fLeft = fPts[0].fX;
806 horizontalEdge->fRight = fPts[fVerb].fX;
807 horizontalEdge->fY = fPts[0].fY;
808 if (horizontalEdge->fLeft > horizontalEdge->fRight) {
809 SkTSwap<SkScalar>(horizontalEdge->fLeft, horizontalEdge->fRight);
810 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000811 if (complete()) {
812 startEdge();
813 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000814 continue;
815 }
816 if (fWinding + winding == 0) {
817 // FIXME: if prior verb or this verb is a horizontal line, reverse
818 // it instead of starting a new edge
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000819 SkASSERT(fCurrentEdge);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000820 if (complete()) {
821 startEdge();
822 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000823 }
824 fWinding = winding;
825 addEdge();
826 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +0000827 if (!complete()) {
828 if (fCurrentEdge) {
829 fEdges.pop_back();
830 }
831 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000832}
833
834private:
835 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000836 InEdge* fCurrentEdge;
837 SkTArray<InEdge>& fEdges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000838 SkTDArray<HorizontalEdge>& fHorizontalEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +0000839 SkPoint fPts[4];
840 SkPath::Verb fVerb;
841 int fPtCount;
842 int fPtOffset;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000843 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +0000844 int8_t fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000845 bool fIgnoreHorizontal;
846};
847
caryclark@google.com6680fb12012-02-07 22:10:51 +0000848struct WorkEdge {
849 SkScalar bottom() const {
850 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +0000851 }
852
caryclark@google.com6680fb12012-02-07 22:10:51 +0000853 void init(const InEdge* edge) {
854 fEdge = edge;
855 fPts = edge->fPts.begin();
856 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +0000857 }
858
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000859 bool advance() {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000860 SkASSERT(fVerb < fEdge->fVerbs.end());
861 fPts += *fVerb++;
862 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +0000863 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000864
865 SkPath::Verb lastVerb() const {
866 SkASSERT(fVerb > fEdge->fVerbs.begin());
867 return (SkPath::Verb) fVerb[-1];
868 }
869
caryclark@google.comc6825902012-02-03 22:07:47 +0000870
871 SkPath::Verb verb() const {
872 return (SkPath::Verb) *fVerb;
873 }
874
caryclark@google.com6008c6562012-02-15 22:01:16 +0000875 ptrdiff_t verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000876 return fVerb - fEdge->fVerbs.begin();
877 }
878
879 int winding() const {
880 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000881 }
882
caryclark@google.com6680fb12012-02-07 22:10:51 +0000883 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +0000884 const SkPoint* fPts;
885 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +0000886};
887
caryclark@google.com6680fb12012-02-07 22:10:51 +0000888// always constructed with SkTDArray because new edges are inserted
889// this may be a inappropriate optimization, suggesting that a separate array of
890// ActiveEdge* may be faster to insert and search
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000891
892// OPTIMIZATION: Brian suggests that global sorting should be unnecessary, since
893// as active edges are introduced, only local sorting should be required
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000894class ActiveEdge {
895public:
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000896 // OPTIMIZATION: fold return statements into one
caryclark@google.com6008c6562012-02-15 22:01:16 +0000897 bool operator<(const ActiveEdge& rh) const {
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000898 if (rh.fAbove.fY - fAbove.fY > fBelow.fY - rh.fAbove.fY
899 && fBelow.fY < rh.fBelow.fY
900 || fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - fAbove.fY
901 && rh.fBelow.fY < fBelow.fY) {
902 // FIXME: need to compute distance, not check for points equal
903 const SkPoint& check = rh.fBelow.fY <= fBelow.fY
904 && fBelow != rh.fBelow ? rh.fBelow :
905 rh.fAbove;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000906 #if COMPARE_DOUBLE
907 SkASSERT(((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
908 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX))
909 == ((check.fY - fDAbove.y) * (fDBelow.x - fDAbove.x)
910 < (fDBelow.y - fDAbove.y) * (check.fX - fDAbove.x)));
911 #endif
912 #if DEBUG_ACTIVE_LESS_THAN
913 SkDebugf("%s 1 %c %cthis (edge=%d) {%g,%g %g,%g}"
914 " < rh (edge=%d) {%g,%g %g,%g} upls=(%d)\n", __FUNCTION__,
915 rh.fBelow.fY <= fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
916 (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
917 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX) ? ' '
918 : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
919 rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
920 UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
921 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX)));
922 #endif
923 #if ASSERT_ON_ULPS
924 int ulps = UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
925 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX));
926 SkASSERT((unsigned) ulps == 0x80000000 || ulps == 0 || ulps > 32);
927 #endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000928 return (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
929 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX);
930 }
931 // FIXME: need to compute distance, not check for points equal
932 const SkPoint& check = fBelow.fY <= rh.fBelow.fY
933 && fBelow != rh.fBelow ? fBelow : fAbove;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000934 #if COMPARE_DOUBLE
935 SkASSERT(((rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
936 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX))
937 == ((rh.fDBelow.y - rh.fDAbove.y) * (check.fX - rh.fDAbove.x)
938 < (check.fY - rh.fDAbove.y) * (rh.fDBelow.x - rh.fDAbove.x)));
939 #endif
940 #if DEBUG_ACTIVE_LESS_THAN
941 SkDebugf("%s 2 %c %cthis (edge=%d) {%g,%g %g,%g}"
942 " < rh (edge=%d) {%g,%g %g,%g} upls=(%d)\n", __FUNCTION__,
943 fBelow.fY <= rh.fBelow.fY & fBelow != rh.fBelow ? 'B' : 'A',
944 (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
945 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)
946 ? ' ' : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
947 rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
948 UlpsDiff((rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX),
949 (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)));
950 #endif
951 #if ASSERT_ON_ULPS
952 int ulps = UlpsDiff((rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX),
953 (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX));
954 SkASSERT((unsigned) ulps == 0x80000000 || ulps == 0 || ulps > 32);
955 #endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000956 return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
957 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000958 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000959
960 // If a pair of edges are nearly coincident for some span, add a T in the
961 // edge so it can be shortened to match the other edge. Note that another
962 // approach is to trim the edge after it is added to the OutBuilder list --
963 // FIXME: since this has no effect if the edge is already done (i.e.,
964 // fYBottom >= y) maybe this can only be done by calling trimLine later.
965 void addTatYBelow(SkScalar y) {
966 if (fBelow.fY <= y || fYBottom >= y) {
967 return;
968 }
969 addTatYInner(y);
970 fFixBelow = true;
971 }
972
973 void addTatYAbove(SkScalar y) {
974 if (fBelow.fY <= y) {
975 return;
976 }
977 addTatYInner(y);
978 }
979
980 void addTatYInner(SkScalar y) {
981 double ts[2];
982 SkScalar left = fWorkEdge.fPts[0].fX;
983 SkScalar right = fWorkEdge.fPts[1].fX;
984 if (left > right) {
985 SkTSwap(left, right);
986 }
987 int pts = LineIntersect(fWorkEdge.fPts, left, right, y, ts);
988 SkASSERT(pts == 1);
989 // An ActiveEdge or WorkEdge has no need to modify the T values computed
990 // in the InEdge, except in the following case. If a pair of edges are
991 // nearly coincident, this may not be detected when the edges are
992 // intersected. Later, when sorted, and this near-coincidence is found,
993 // an additional t value must be added, requiring the cast below.
994 InEdge* writable = const_cast<InEdge*>(fWorkEdge.fEdge);
995 int insertedAt = writable->add(ts, pts, fWorkEdge.verbIndex());
996 if (insertedAt >= 0) {
997 if (insertedAt + 1 < fTIndex) {
998 SkASSERT(insertedAt + 2 == fTIndex);
999 --fTIndex;
1000 }
1001 }
1002 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001003
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001004 bool advanceT() {
1005 SkASSERT(fTIndex <= fTs->count());
1006 return ++fTIndex <= fTs->count();
1007 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001008
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001009 bool advance() {
1010 // FIXME: flip sense of next
1011 bool result = fWorkEdge.advance();
1012 fDone = !result;
1013 initT();
1014 return result;
1015 }
1016
1017 void calcLeft(SkScalar y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001018 // OPTIMIZE: put a kDone_Verb at the end of the verb list?
caryclark@google.com4917f172012-03-05 22:01:21 +00001019 if (fDone || fBelow.fY > y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001020 return; // nothing to do; use last
caryclark@google.com4917f172012-03-05 22:01:21 +00001021 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001022 calcLeft();
1023 }
1024
1025 void calcLeft() {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001026 switch (fWorkEdge.verb()) {
1027 case SkPath::kLine_Verb: {
1028 // OPTIMIZATION: if fXAbove, fXBelow have already been computed
1029 // for the fTIndex, don't do it again
1030 // For identical x, this lets us know which edge is first.
1031 // If both edges have T values < 1, check x at next T (fXBelow).
1032 int add = (fTIndex <= fTs->count()) - 1;
1033 double tAbove = t(fTIndex + add);
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001034 // OPTIMIZATION: may not need Y
1035 LineXYAtT(fWorkEdge.fPts, tAbove, &fAbove);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001036 double tBelow = t(fTIndex - ~add);
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001037 LineXYAtT(fWorkEdge.fPts, tBelow, &fBelow);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001038 SkASSERT(tAbove != tBelow);
1039// maybe the following is the right sort of thing to do, but it's fragile and
1040// it breaks testSimplifySkinnyTriangle4
1041#if 0
1042 if (fAbove == fBelow && !add) {
1043 tBelow = t(fTIndex - ~add + 1);
1044 LineXYAtT(fWorkEdge.fPts, tBelow, &fBelow);
1045 }
1046#endif
1047 #if COMPARE_DOUBLE
1048 LineXYAtT(fWorkEdge.fPts, tAbove, &fDAbove);
1049 LineXYAtT(fWorkEdge.fPts, tBelow, &fDBelow);
1050 #endif
1051 #if DEBUG_ABOVE_BELOW
1052 fTAbove = tAbove;
1053 fTBelow = tBelow;
1054 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001055 break;
1056 }
1057 default:
1058 // FIXME: add support for all curve types
1059 SkASSERT(0);
1060 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001061 }
1062
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001063 bool done(SkScalar y) const {
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001064 return fDone || fYBottom > y;
1065 }
1066
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001067 void fixBelow() {
1068 if (fFixBelow) {
1069 LineXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1070 fFixBelow = false;
1071 }
1072 }
1073
caryclark@google.com6680fb12012-02-07 22:10:51 +00001074 void init(const InEdge* edge) {
1075 fWorkEdge.init(edge);
1076 initT();
caryclark@google.com4917f172012-03-05 22:01:21 +00001077 fBelow.fY = SK_ScalarMin;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001078 fDone = false;
1079 fYBottom = SK_ScalarMin;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001080 }
1081
1082 void initT() {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001083 const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
1084 SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
1085 const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
1086 fTs = &interceptPtr->fTs;
1087 // the above is conceptually the same as
1088 // fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
1089 // but templated arrays don't allow returning a pointer to the end() element
caryclark@google.com6680fb12012-02-07 22:10:51 +00001090 fTIndex = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +00001091 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001092
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001093 // OPTIMIZATION: record if two edges are coincident when the are intersected
1094 // It's unclear how to do this -- seems more complicated than recording the
1095 // t values, since the same t values could exist intersecting non-coincident
1096 // edges.
1097 bool isCoincidentWith(const ActiveEdge* edge, SkScalar y) const {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001098
1099 if (!fAbove.equalsWithinTolerance(edge->fAbove, MIN_PT_DELTA)
1100 || !fBelow.equalsWithinTolerance(edge->fBelow, MIN_PT_DELTA)) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001101 return false;
1102 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001103 uint8_t verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
1104 uint8_t edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb()
1105 : edge->fWorkEdge.verb();
1106 if (verb != edgeVerb) {
1107 return false;
1108 }
1109 switch (verb) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001110 case SkPath::kLine_Verb: {
caryclark@google.com4917f172012-03-05 22:01:21 +00001111 return true;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001112 }
1113 default:
1114 // FIXME: add support for all curve types
1115 SkASSERT(0);
1116 }
1117 return false;
1118 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001119
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001120 // The shortest close call edge should be moved into a position where
1121 // it contributes if the winding is transitioning to or from zero.
1122 bool swapClose(const ActiveEdge* next, int prev, int wind, int mask) const {
1123 if ((prev & mask) == 0 || (wind & mask) == 0) {
1124 return next->fBelow.fY < fBelow.fY;
1125 }
1126 int nextWinding = wind + next->fWorkEdge.winding();
1127 if ((nextWinding & mask) == 0) {
1128 return fBelow.fY < next->fBelow.fY;
1129 }
1130 return false;
1131 }
1132
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001133 bool swapCoincident(const ActiveEdge* edge, SkScalar bottom) const {
1134 if (fBelow.fY >= bottom || fDone || edge->fDone) {
1135 return false;
1136 }
1137 ActiveEdge thisWork = *this;
1138 ActiveEdge edgeWork = *edge;
1139 while ((thisWork.advanceT() || thisWork.advance())
1140 && (edgeWork.advanceT() || edgeWork.advance())) {
1141 thisWork.calcLeft();
1142 edgeWork.calcLeft();
1143 if (thisWork < edgeWork) {
1144 return false;
1145 }
1146 if (edgeWork < thisWork) {
1147 return true;
1148 }
1149 }
1150 return false;
1151 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001152
1153 bool tooCloseToCall(const ActiveEdge* edge) const {
1154 int ulps;
1155 // FIXME: don't compare points for equality
1156 // OPTIMIZATION: refactor to make one call to UlpsDiff
1157 if (edge->fAbove.fY - fAbove.fY > fBelow.fY - edge->fAbove.fY
1158 && fBelow.fY < edge->fBelow.fY
1159 || fAbove.fY - edge->fAbove.fY < edge->fBelow.fY - fAbove.fY
1160 && edge->fBelow.fY < fBelow.fY) {
1161 const SkPoint& check = edge->fBelow.fY <= fBelow.fY
1162 && fBelow != edge->fBelow ? edge->fBelow :
1163 edge->fAbove;
1164 ulps = UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
1165 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX));
1166 } else {
1167 const SkPoint& check = fBelow.fY <= edge->fBelow.fY
1168 && fBelow != edge->fBelow ? fBelow : fAbove;
1169 ulps = UlpsDiff((edge->fBelow.fY - edge->fAbove.fY)
1170 * (check.fX - edge->fAbove.fX),
1171 (check.fY - edge->fAbove.fY)
1172 * (edge->fBelow.fX - edge->fAbove.fX));
1173 }
1174 return ulps >= 0 && ulps <= 32;
1175 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001176
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001177 double nextT() const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001178 SkASSERT(fTIndex <= fTs->count());
1179 return t(fTIndex + 1);
1180 }
caryclark@google.com6680fb12012-02-07 22:10:51 +00001181
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001182 double t() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001183 if (fTIndex == 0) {
1184 return 0;
1185 }
1186 if (fTIndex > fTs->count()) {
1187 return 1;
1188 }
1189 return (*fTs)[fTIndex - 1];
1190 }
1191
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001192 double t(int tIndex) const {
1193 if (tIndex == 0) {
1194 return 0;
1195 }
1196 if (tIndex > fTs->count()) {
1197 return 1;
1198 }
1199 return (*fTs)[tIndex - 1];
1200 }
1201
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001202 // FIXME: debugging only
1203 int ID() {
1204 return fWorkEdge.fEdge->fID;
1205 }
1206
1207public:
caryclark@google.com6680fb12012-02-07 22:10:51 +00001208 WorkEdge fWorkEdge;
1209 const SkTDArray<double>* fTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001210 SkPoint fAbove;
1211 SkPoint fBelow;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001212#if COMPARE_DOUBLE
1213 _Point fDAbove;
1214 _Point fDBelow;
1215#endif
1216#if DEBUG_ABOVE_BELOW
1217 double fTAbove;
1218 double fTBelow;
1219#endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001220 SkScalar fYBottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001221 int fCoincident;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001222 int fTIndex;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001223 bool fSkip; // OPTIMIZATION: use bitfields?
1224 bool fCloseCall;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001225 bool fDone;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001226 bool fFixBelow;
caryclark@google.comc6825902012-02-03 22:07:47 +00001227};
1228
caryclark@google.com6680fb12012-02-07 22:10:51 +00001229static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001230 size_t count = activeEdges.count();
1231 for (size_t index = 0; index < count; ++index) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001232 if (edge == activeEdges[index].fWorkEdge.fEdge) {
1233 return;
1234 }
1235 }
1236 ActiveEdge* active = activeEdges.append();
1237 active->init(edge);
1238}
1239
caryclark@google.com4917f172012-03-05 22:01:21 +00001240// Find any intersections in the range of active edges. A pair of edges, on
1241// either side of another edge, may change the winding contribution for part of
1242// the edge.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001243// Keep horizontal edges just for
caryclark@google.com4917f172012-03-05 22:01:21 +00001244// the purpose of computing when edges change their winding contribution, since
1245// this is essentially computing the horizontal intersection.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001246static void addBottomT(InEdge** currentPtr, InEdge** lastPtr,
1247 HorizontalEdge** horizontal) {
1248 InEdge** testPtr = currentPtr - 1;
1249 HorizontalEdge* horzEdge = *horizontal;
1250 SkScalar left = horzEdge->fLeft;
1251 SkScalar bottom = horzEdge->fY;
1252 while (++testPtr != lastPtr) {
1253 InEdge* test = *testPtr;
1254 if (test->fBounds.fBottom <= bottom || test->fBounds.fRight <= left) {
1255 continue;
1256 }
1257 WorkEdge wt;
1258 wt.init(test);
1259 do {
1260 HorizontalEdge** sorted = horizontal;
1261 horzEdge = *sorted;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001262 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001263 if (wt.verb() == SkPath::kLine_Verb) {
1264 double wtTs[2];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001265 int pts = LineIntersect(wt.fPts, horzEdge->fLeft,
1266 horzEdge->fRight, horzEdge->fY, wtTs);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001267 if (pts) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001268#if DEBUG_ADD_BOTTOM_TS
1269 SkDebugf("%s y=%g wtTs[0]=%g (%g,%g, %g,%g)\n", __FUNCTION__,
1270 horzEdge->fY, wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
1271 wt.fPts[1].fX, wt.fPts[1].fY);
1272 if (pts == 2) {
1273 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1274 }
1275#endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001276 test->add(wtTs, pts, wt.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001277 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001278 } else {
1279 // FIXME: add all curve types
1280 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001281 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001282 horzEdge = *++sorted;
1283 } while (horzEdge->fY == bottom
1284 && horzEdge->fLeft <= test->fBounds.fRight);
1285 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001286 }
1287}
1288
1289static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001290 InEdge** testPtr = currentPtr - 1;
1291 while (++testPtr != lastPtr - 1) {
1292 InEdge* test = *testPtr;
1293 InEdge** nextPtr = testPtr;
1294 do {
1295 InEdge* next = *++nextPtr;
1296 if (test->cached(next)) {
1297 continue;
1298 }
1299 if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
1300 continue;
1301 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001302 WorkEdge wt, wn;
1303 wt.init(test);
1304 wn.init(next);
1305 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001306 if (wt.verb() == SkPath::kLine_Verb
1307 && wn.verb() == SkPath::kLine_Verb) {
1308 double wtTs[2], wnTs[2];
1309 int pts = LineIntersect(wt.fPts, wn.fPts, wtTs, wnTs);
1310 if (pts) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001311#if DEBUG_ADD_INTERSECTING_TS
1312 SkPoint wtOutPt, wnOutPt;
1313 LineXYAtT(wt.fPts, wtTs[0], &wtOutPt);
1314 LineXYAtT(wn.fPts, wnTs[0], &wnOutPt);
1315 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
1316 __FUNCTION__,
1317 wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
1318 wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY,
1319 test->fID, next->fID);
1320 if (pts == 2) {
1321 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1322 }
1323 SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
1324 __FUNCTION__,
1325 wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY,
1326 wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY,
1327 test->fID, next->fID);
1328 if (pts == 2) {
1329 SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
1330 }
1331#endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001332 test->add(wtTs, pts, wt.verbIndex());
1333 next->add(wnTs, pts, wn.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001334 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001335 } else {
1336 // FIXME: add all combinations of curve types
1337 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001338 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001339 } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
1340 } while (nextPtr != lastPtr);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001341 }
1342}
1343
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001344static InEdge** advanceEdges(SkTDArray<ActiveEdge>* activeEdges,
caryclark@google.com6008c6562012-02-15 22:01:16 +00001345 InEdge** currentPtr, InEdge** lastPtr, SkScalar y) {
1346 InEdge** testPtr = currentPtr - 1;
1347 while (++testPtr != lastPtr) {
1348 if ((*testPtr)->fBounds.fBottom > y) {
1349 continue;
1350 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001351 if (activeEdges) {
1352 InEdge* test = *testPtr;
1353 ActiveEdge* activePtr = activeEdges->begin() - 1;
1354 ActiveEdge* lastActive = activeEdges->end();
1355 while (++activePtr != lastActive) {
1356 if (activePtr->fWorkEdge.fEdge == test) {
1357 activeEdges->remove(activePtr - activeEdges->begin());
1358 break;
1359 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001360 }
1361 }
1362 if (testPtr == currentPtr) {
1363 ++currentPtr;
1364 }
1365 }
1366 return currentPtr;
1367}
1368
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001369// OPTIMIZE: inline?
1370static HorizontalEdge** advanceHorizontal(HorizontalEdge** edge, SkScalar y) {
1371 while ((*edge)->fY < y) {
1372 ++edge;
1373 }
1374 return edge;
1375}
1376
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001377// compute bottom taking into account any intersected edges
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001378static SkScalar computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
1379 SkScalar y, SkScalar bottom) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001380 ActiveEdge* activePtr = activeEdges.begin() - 1;
1381 ActiveEdge* lastActive = activeEdges.end();
1382 while (++activePtr != lastActive) {
1383 const InEdge* test = activePtr->fWorkEdge.fEdge;
1384 if (!test->fContainsIntercepts) {
1385 continue;
1386 }
1387 WorkEdge wt;
1388 wt.init(test);
1389 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001390 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
caryclark@google.com4917f172012-03-05 22:01:21 +00001391 if (intercepts.fTopIntercepts > 1) {
1392 SkScalar yTop = wt.fPts[0].fY;
1393 if (yTop > y && bottom > yTop) {
1394 bottom = yTop;
1395 }
1396 }
1397 if (intercepts.fBottomIntercepts > 1) {
1398 SkScalar yBottom = wt.fPts[wt.verb()].fY;
1399 if (yBottom > y && bottom > yBottom) {
1400 bottom = yBottom;
1401 }
1402 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001403 const SkTDArray<double>& fTs = intercepts.fTs;
1404 size_t count = fTs.count();
1405 for (size_t index = 0; index < count; ++index) {
1406 if (wt.verb() == SkPath::kLine_Verb) {
1407 SkScalar yIntercept = LineYAtT(wt.fPts, fTs[index]);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001408 if (yIntercept > y && bottom > yIntercept) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001409 bottom = yIntercept;
1410 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001411 } else {
1412 // FIXME: add all curve types
1413 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001414 }
1415 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001416 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001417 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001418 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001419}
1420
1421static SkScalar findBottom(InEdge** currentPtr,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001422 InEdge** edgeListEnd, SkTDArray<ActiveEdge>* activeEdges, SkScalar y,
caryclark@google.com6008c6562012-02-15 22:01:16 +00001423 bool asFill, InEdge**& testPtr) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001424 InEdge* current = *currentPtr;
1425 SkScalar bottom = current->fBounds.fBottom;
1426
1427 // find the list of edges that cross y
caryclark@google.com6008c6562012-02-15 22:01:16 +00001428 InEdge* test = *testPtr;
1429 while (testPtr != edgeListEnd) {
1430 SkScalar testTop = test->fBounds.fTop;
1431 if (bottom <= testTop) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001432 break;
1433 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001434 SkScalar testBottom = test->fBounds.fBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001435 // OPTIMIZATION: Shortening the bottom is only interesting when filling
1436 // and when the edge is to the left of a longer edge. If it's a framing
1437 // edge, or part of the right, it won't effect the longer edges.
caryclark@google.com6008c6562012-02-15 22:01:16 +00001438 if (testTop > y) {
1439 bottom = testTop;
1440 break;
1441 }
1442 if (y < testBottom) {
1443 if (bottom > testBottom) {
1444 bottom = testBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001445 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001446 if (activeEdges) {
1447 addToActive(*activeEdges, test);
1448 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001449 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001450 test = *++testPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001451 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001452 return bottom;
1453}
1454
1455static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
1456 SkTDArray<InEdge*>& edgeList) {
1457 size_t edgeCount = edges.count();
1458 if (edgeCount == 0) {
1459 return;
1460 }
1461 for (size_t index = 0; index < edgeCount; ++index) {
1462 *edgeList.append() = &edges[index];
1463 }
1464 edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1465 *edgeList.append() = &edgeSentinel;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001466 QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001467}
1468
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001469static void makeHorizontalList(SkTDArray<HorizontalEdge>& edges,
1470 HorizontalEdge& edgeSentinel, SkTDArray<HorizontalEdge*>& edgeList) {
1471 size_t edgeCount = edges.count();
1472 if (edgeCount == 0) {
1473 return;
1474 }
1475 for (size_t index = 0; index < edgeCount; ++index) {
1476 *edgeList.append() = &edges[index];
1477 }
1478 edgeSentinel.fLeft = edgeSentinel.fRight = edgeSentinel.fY = SK_ScalarMax;
1479 *edgeList.append() = &edgeSentinel;
1480 QSort<HorizontalEdge>(edgeList.begin(), edgeList.end() - 1);
1481}
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001482
1483static void skipCoincidence(int lastWinding, int winding, int windingMask,
1484 ActiveEdge* activePtr, ActiveEdge* firstCoincident) {
1485 if (((lastWinding & windingMask) == 0) ^ (winding & windingMask) != 0) {
1486 return;
1487 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001488 // FIXME: ? shouldn't this be if (lastWinding & windingMask) ?
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001489 if (lastWinding) {
1490 activePtr->fSkip = false;
1491 } else {
1492 firstCoincident->fSkip = false;
1493 }
1494}
1495
caryclark@google.com6008c6562012-02-15 22:01:16 +00001496static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001497 SkTDArray<ActiveEdge*>& edgeList, SkScalar y) {
1498 const int tab = 3; // FIXME: debugging only
caryclark@google.com6008c6562012-02-15 22:01:16 +00001499 size_t edgeCount = activeEdges.count();
1500 if (edgeCount == 0) {
1501 return;
1502 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001503#if DEBUG_SORT_HORIZONTAL
1504 SkDebugf("%s y=%1.9g\n", __FUNCTION__, y);
1505#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00001506 size_t index;
1507 for (index = 0; index < edgeCount; ++index) {
1508 ActiveEdge& activeEdge = activeEdges[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001509 do {
1510 activeEdge.calcLeft(y);
1511 // skip segments that don't span y
1512 if (activeEdge.fAbove != activeEdge.fBelow) {
1513 break;
1514 }
1515 if (activeEdge.fDone) {
1516#if DEBUG_SORT_HORIZONTAL
1517 SkDebugf("%*s edge=%d done\n", tab, "", activeEdge.ID());
1518#endif
1519 goto nextEdge;
1520 }
1521#if DEBUG_SORT_HORIZONTAL
1522 SkDebugf("%*s edge=%d above==below\n", tab, "", activeEdge.ID());
1523#endif
1524 } while (activeEdge.advanceT() || activeEdge.advance());
1525#if DEBUG_SORT_HORIZONTAL
1526 SkDebugf("%*s edge=%d above=(%1.9g,%1.9g) (%1.9g) below=(%1.9g,%1.9g)"
1527 " (%1.9g)\n", tab, "", activeEdge.ID(),
1528 activeEdge.fAbove.fX, activeEdge.fAbove.fY, activeEdge.fTAbove,
1529 activeEdge.fBelow.fX, activeEdge.fBelow.fY, activeEdge.fTBelow);
1530#endif
1531 activeEdge.fSkip = activeEdge.fCloseCall = activeEdge.fFixBelow = false;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001532 *edgeList.append() = &activeEdge;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001533nextEdge:
1534 ;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001535 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001536 QSort<ActiveEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001537}
1538
1539// remove coincident edges
1540// OPTIMIZE: remove edges? This is tricky because the current logic expects
1541// the winding count to be maintained while skipping coincident edges. In
1542// addition to removing the coincident edges, the remaining edges would need
1543// to have a different winding value, possibly different per intercept span.
1544static SkScalar adjustCoincident(SkTDArray<ActiveEdge*>& edgeList,
1545 int windingMask, SkScalar y, SkScalar bottom, OutEdgeBuilder& outBuilder)
1546{
1547#if DEBUG_ADJUST_COINCIDENT
1548 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
1549#endif
1550 size_t edgeCount = edgeList.count();
1551 if (edgeCount == 0) {
1552 return bottom;
1553 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001554 ActiveEdge* activePtr = edgeList[0];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001555 size_t index;
1556 bool foundCoincident = false;
1557 int firstIndex = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001558 for (index = 1; index < edgeCount; ++index) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001559 ActiveEdge* nextPtr = edgeList[index];
1560 bool closeCall = false;
1561 activePtr->fCoincident = firstIndex;
1562 if (activePtr->isCoincidentWith(nextPtr, y)
1563 || (closeCall = activePtr->tooCloseToCall(nextPtr))) {
1564 activePtr->fSkip = nextPtr->fSkip = foundCoincident = true;
1565 activePtr->fCloseCall = nextPtr->fCloseCall = closeCall;
1566 } else {
1567 firstIndex = index;
1568 }
1569 activePtr = nextPtr;
1570 }
1571 activePtr->fCoincident = firstIndex;
1572 if (!foundCoincident) {
1573 return bottom;
1574 }
1575 int winding = 0;
1576 activePtr = edgeList[0];
1577 for (index = 1; index < edgeCount; ++index) {
1578 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001579 winding += activePtr->fWorkEdge.winding();
caryclark@google.com6008c6562012-02-15 22:01:16 +00001580 ActiveEdge* nextPtr = edgeList[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001581 if (activePtr->fCoincident == nextPtr->fCoincident) {
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001582 // the coincident edges may not have been sorted above -- advance
1583 // the edges and resort if needed
1584 // OPTIMIZE: if sorting is done incrementally as new edges are added
1585 // and not all at once as is done here, fold this test into the
1586 // current less than test.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001587 if (activePtr->fCloseCall ? activePtr->swapClose(nextPtr,
1588 priorWinding, winding, windingMask)
1589 : activePtr->swapCoincident(nextPtr, bottom)) {
caryclark@google.com4917f172012-03-05 22:01:21 +00001590 winding -= activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001591 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
1592 SkTSwap<ActiveEdge*>(activePtr, nextPtr);
caryclark@google.com4917f172012-03-05 22:01:21 +00001593 winding += activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001594 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001595 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001596 activePtr = nextPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001597 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001598 int firstCoincidentWinding = 0;
1599 ActiveEdge* firstCoincident = NULL;
1600 winding = 0;
1601 activePtr = edgeList[0];
1602 for (index = 1; index < edgeCount; ++index) {
1603 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001604 winding += activePtr->fWorkEdge.winding();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001605 ActiveEdge* nextPtr = edgeList[index];
1606 if (activePtr->fCoincident == nextPtr->fCoincident) {
1607 if (!firstCoincident) {
1608 firstCoincident = activePtr;
1609 firstCoincidentWinding = priorWinding;
1610 }
1611 if (activePtr->fCloseCall) {
1612 // If one of the edges has already been added to out as a non
1613 // coincident edge, trim it back to the top of this span
1614 if (outBuilder.trimLine(y, activePtr->ID())) {
1615 activePtr->addTatYAbove(y);
1616 activePtr->fYBottom = y;
1617 }
1618 if (outBuilder.trimLine(y, nextPtr->ID())) {
1619 nextPtr->addTatYAbove(y);
1620 nextPtr->fYBottom = y;
1621 }
1622 // add missing t values so edges can be the same length
1623 SkScalar testY = activePtr->fBelow.fY;
1624 nextPtr->addTatYBelow(testY);
1625 if (bottom > testY && testY > y) {
1626 bottom = testY;
1627 }
1628 testY = nextPtr->fBelow.fY;
1629 activePtr->addTatYBelow(testY);
1630 if (bottom > testY && testY > y) {
1631 bottom = testY;
1632 }
1633 }
1634 } else if (firstCoincident) {
1635 skipCoincidence(firstCoincidentWinding, winding, windingMask,
1636 activePtr, firstCoincident);
1637 firstCoincident = NULL;
1638 }
1639 activePtr = nextPtr;
1640 }
1641 if (firstCoincident) {
1642 winding += activePtr->fWorkEdge.winding();
1643 skipCoincidence(firstCoincidentWinding, winding, windingMask, activePtr,
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001644 firstCoincident);
1645 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001646 // fix up the bottom for close call edges. OPTIMIZATION: maybe this could
1647 // be in the loop above, but moved here since loop above reads fBelow and
1648 // it felt unsafe to write it in that loop
1649 for (index = 0; index < edgeCount; ++index) {
1650 (edgeList[index])->fixBelow();
1651 }
1652 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001653}
1654
1655// stitch edge and t range that satisfies operation
caryclark@google.com6008c6562012-02-15 22:01:16 +00001656static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001657 SkScalar bottom, int windingMask, OutEdgeBuilder& outBuilder) {
1658 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001659 ActiveEdge** activeHandle = edgeList.begin() - 1;
1660 ActiveEdge** lastActive = edgeList.end();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001661 const int tab = 7; // FIXME: debugging only
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001662 if (gShowDebugf) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001663 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001664 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001665 while (++activeHandle != lastActive) {
1666 ActiveEdge* activePtr = *activeHandle;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001667 const WorkEdge& wt = activePtr->fWorkEdge;
1668 int lastWinding = winding;
1669 winding += wt.winding();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001670 if (gShowDebugf) {
1671 SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d"
1672#if DEBUG_ABOVE_BELOW
1673 " above=%1.9g below=%1.9g"
1674#endif
1675 "\n",
1676 tab-4, "", activePtr->ID(), lastWinding,
1677 winding, activePtr->fSkip, activePtr->fCloseCall
1678#if DEBUG_ABOVE_BELOW
1679 , activePtr->fTAbove, activePtr->fTBelow
1680#endif
1681 );
1682 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001683 if (activePtr->done(y)) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001684 if (activePtr->fCloseCall) {
1685 // if the top has already advanced, trim the last edge add
1686 // FIXME: not so simple
1687 outBuilder.trimLine(y, activePtr->ID());
1688 activePtr->fYBottom = y;
1689 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001690 // FIXME: if this is successful, rewrite done to take bottom as well
1691 if (activePtr->fDone) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001692 if (gShowDebugf) {
1693 SkDebugf("%*s activePtr->fDone\n", tab, "");
1694 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001695 continue;
1696 }
1697 if (activePtr->fYBottom >= bottom) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001698 if (gShowDebugf) {
1699 SkDebugf("%*s activePtr->fYBottom=%1.9g >= bottom\n", tab, "",
1700 activePtr->fYBottom);
1701 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001702 continue;
1703 }
1704 if (0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001705 SkDebugf("%s bot %1.9g,%1.9g\n", __FUNCTION__, activePtr->fYBottom,
1706 bottom);
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001707 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001708 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00001709 int opener = (lastWinding & windingMask) == 0;
1710 bool closer = (winding & windingMask) == 0;
1711 SkASSERT(!opener | !closer);
1712 bool inWinding = opener | closer;
caryclark@google.com4917f172012-03-05 22:01:21 +00001713 SkPoint clippedPts[2];
1714 const SkPoint* clipped = NULL;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001715 #if COMPARE_DOUBLE
1716 _Line dPoints;
1717 _Line clippedDPts;
1718 const _Point* dClipped = NULL;
1719 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001720 uint8_t verb = wt.verb();
1721 bool moreToDo, aboveBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001722 do {
1723 double currentT = activePtr->t();
caryclark@google.com6008c6562012-02-15 22:01:16 +00001724 SkASSERT(currentT < 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001725 const SkPoint* points = wt.fPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001726 double nextT;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001727 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001728 nextT = activePtr->nextT();
1729 if (verb == SkPath::kLine_Verb) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001730 // FIXME: obtuse: want efficient way to say
1731 // !currentT && currentT != 1 || !nextT && nextT != 1
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001732 if (currentT * nextT != 0 || currentT + nextT != 1) {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001733 // OPTIMIZATION: if !inWinding, we only need
1734 // clipped[1].fY
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001735 LineSubDivide(points, currentT, nextT, clippedPts);
1736 clipped = clippedPts;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001737 #if COMPARE_DOUBLE
1738 LineSubDivide(points, currentT, nextT, clippedDPts);
1739 dClipped = clippedDPts;
1740 #endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001741 } else {
1742 clipped = points;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001743 #if COMPARE_DOUBLE
1744 dPoints[0].x = SkScalarToDouble(points[0].fX);
1745 dPoints[0].y = SkScalarToDouble(points[1].fY);
1746 dPoints[1].x = SkScalarToDouble(points[0].fX);
1747 dPoints[1].y = SkScalarToDouble(points[1].fY);
1748 dClipped = dPoints;
1749 #endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001750 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001751 if (inWinding && !activePtr->fSkip) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001752 if (gShowDebugf) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001753 SkDebugf("%*s line %1.9g,%1.9g %1.9g,%1.9g edge=%d"
1754 " v=%d t=(%1.9g,%1.9g)\n", tab, "",
caryclark@google.comc17972e2012-02-20 21:33:22 +00001755 clipped[0].fX, clipped[0].fY,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001756 clipped[1].fX, clipped[1].fY,
1757 activePtr->ID(),
1758 activePtr->fWorkEdge.fVerb
1759 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
1760 currentT, nextT);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001761 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001762 outBuilder.addLine(clipped, activePtr->fWorkEdge.fEdge->fID,
1763 activePtr->fCloseCall);
1764 } else {
1765 if (gShowDebugf) {
1766 SkDebugf("%*s skip %1.9g,%1.9g %1.9g,%1.9g"
1767 " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
1768 clipped[0].fX, clipped[0].fY,
1769 clipped[1].fX, clipped[1].fY,
1770 activePtr->ID(),
1771 activePtr->fWorkEdge.fVerb
1772 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
1773 currentT, nextT);
1774 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001775 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001776 // by advancing fAbove/fBelow, the next call to sortHorizontal
1777 // will use these values if they're still valid instead of
1778 // recomputing
1779 #if COMPARE_DOUBLE
1780 SkASSERT((clipped[1].fY > activePtr->fBelow.fY
1781 && bottom >= activePtr->fBelow.fY)
1782 == (dClipped[1].y > activePtr->fDBelow.y
1783 && bottom >= activePtr->fDBelow.y));
1784 #endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001785 if (clipped[1].fY > activePtr->fBelow.fY
1786 && bottom >= activePtr->fBelow.fY ) {
1787 activePtr->fAbove = activePtr->fBelow;
1788 activePtr->fBelow = clipped[1];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001789 #if COMPARE_DOUBLE
1790 activePtr->fDAbove = activePtr->fDBelow;
1791 activePtr->fDBelow = dClipped[1];
1792 #endif
1793 #if DEBUG_ABOVE_BELOW
1794 activePtr->fTAbove = activePtr->fTBelow;
1795 activePtr->fTBelow = nextT;
1796 #endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001797 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001798 } else {
1799 // FIXME: add all curve types
1800 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001801 }
1802 currentT = nextT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001803 moreToDo = activePtr->advanceT();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001804 activePtr->fYBottom = clipped[verb].fY; // was activePtr->fCloseCall ? bottom :
1805
1806 // clearing the fSkip/fCloseCall bit here means that trailing edges
1807 // fall out of sync, if one edge is long and another is a series of short pieces
1808 // if fSkip/fCloseCall is set, need to recompute coincidence/too-close-to-call
1809 // after advancing
1810 // another approach would be to restrict bottom to smaller part of close call
1811 // maybe this is already happening with coincidence when intersection is computed,
1812 // and needs to be added to the close call computation as well
1813 // this is hard to do because that the bottom is important is not known when
1814 // the lines are intersected; only when the computation for edge sorting is done
1815 // does the need for new bottoms become apparent.
1816 // maybe this is good incentive to scrap the current sort and do an insertion
1817 // sort that can take this into consideration when the x value is computed
1818
1819 // FIXME: initialized in sortHorizontal, cleared here as well so
1820 // that next edge is not skipped -- but should skipped edges ever
1821 // continue? (probably not)
1822 aboveBottom = clipped[verb].fY < bottom && !activePtr->fCloseCall; // TEST: added close call
1823 activePtr->fSkip = activePtr->fCloseCall = false;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001824 } while (moreToDo & aboveBottom);
1825 } while ((moreToDo || activePtr->advance()) & aboveBottom);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001826 }
1827}
1828
caryclark@google.comc6825902012-02-03 22:07:47 +00001829void simplify(const SkPath& path, bool asFill, SkPath& simple) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001830 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
1831 int windingMask = (path.getFillType() & 1) ? 1 : -1;
1832 simple.reset();
1833 simple.setFillType(SkPath::kEvenOdd_FillType);
caryclark@google.comc6825902012-02-03 22:07:47 +00001834 // turn path into list of edges increasing in y
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001835 // if an edge is a quad or a cubic with a y extrema, note it, but leave it
1836 // unbroken. Once we have a list, sort it, then walk the list (walk edges
1837 // twice that have y extrema's on top) and detect crossings -- look for raw
1838 // bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +00001839 SkTArray<InEdge> edges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001840 SkTDArray<HorizontalEdge> horizontalEdges;
1841 InEdgeBuilder builder(path, asFill, edges, horizontalEdges);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001842 SkTDArray<InEdge*> edgeList;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001843 InEdge edgeSentinel;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001844 makeEdgeList(edges, edgeSentinel, edgeList);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001845 SkTDArray<HorizontalEdge*> horizontalList;
1846 HorizontalEdge horizontalSentinel;
1847 makeHorizontalList(horizontalEdges, horizontalSentinel, horizontalList);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001848 InEdge** currentPtr = edgeList.begin();
caryclark@google.com4917f172012-03-05 22:01:21 +00001849 if (!currentPtr) {
1850 return;
1851 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001852 // find all intersections between edges
1853// beyond looking for horizontal intercepts, we need to know if any active edges
1854// intersect edges below 'bottom', but above the active edge segment.
1855// maybe it makes more sense to compute all intercepts before doing anything
1856// else, since the intercept list is long-lived, at least in the current design.
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001857 SkScalar y = (*currentPtr)->fBounds.fTop;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001858 HorizontalEdge** currentHorizontal = horizontalList.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00001859 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001860 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001861 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001862 NULL, y, asFill, lastPtr);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001863 if (lastPtr > currentPtr) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001864 if (currentHorizontal) {
1865 if ((*currentHorizontal)->fY < SK_ScalarMax) {
1866 addBottomT(currentPtr, lastPtr, currentHorizontal);
1867 }
1868 currentHorizontal = advanceHorizontal(currentHorizontal, bottom);
1869 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00001870 addIntersectingTs(currentPtr, lastPtr);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001871 }
1872 y = bottom;
1873 currentPtr = advanceEdges(NULL, currentPtr, lastPtr, y);
1874 } while (*currentPtr != &edgeSentinel);
1875
1876#if DEBUG_DUMP
1877 InEdge** debugPtr = edgeList.begin();
1878 do {
1879 (*debugPtr++)->dump();
1880 } while (*debugPtr != &edgeSentinel);
1881#endif
1882
1883 // walk the sorted edges from top to bottom, computing accumulated winding
1884 SkTDArray<ActiveEdge> activeEdges;
1885 OutEdgeBuilder outBuilder(asFill);
1886 currentPtr = edgeList.begin();
1887 y = (*currentPtr)->fBounds.fTop;
1888 do {
1889 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
1890 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
1891 &activeEdges, y, asFill, lastPtr);
1892 if (lastPtr > currentPtr) {
1893 bottom = computeInterceptBottom(activeEdges, y, bottom);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001894 SkTDArray<ActiveEdge*> activeEdgeList;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001895 sortHorizontal(activeEdges, activeEdgeList, y);
1896 bottom = adjustCoincident(activeEdgeList, windingMask, y, bottom,
1897 outBuilder);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001898 stitchEdge(activeEdgeList, y, bottom, windingMask, outBuilder);
1899 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001900 y = bottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001901 // OPTIMIZATION: as edges expire, InEdge allocations could be released
1902 currentPtr = advanceEdges(&activeEdges, currentPtr, lastPtr, y);
caryclark@google.comc6825902012-02-03 22:07:47 +00001903 } while (*currentPtr != &edgeSentinel);
caryclark@google.comc6825902012-02-03 22:07:47 +00001904 // assemble output path from string of pts, verbs
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001905 outBuilder.bridge();
1906 outBuilder.assemble(simple);
caryclark@google.comc6825902012-02-03 22:07:47 +00001907}