blob: 7f0ddffc0110d097128826fc0351142b54ad89f3 [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.coma5764232012-03-28 16:20:21 +000010#include "Intersections.h"
caryclark@google.comc6825902012-02-03 22:07:47 +000011#include "LineIntersection.h"
12#include "SkPath.h"
13#include "SkRect.h"
14#include "SkTArray.h"
15#include "SkTDArray.h"
caryclark@google.comd88e0892012-03-27 13:23:51 +000016#include "ShapeOps.h"
caryclark@google.comc6825902012-02-03 22:07:47 +000017#include "TSearch.h"
18
caryclark@google.coma5764232012-03-28 16:20:21 +000019#if 01 // set to 1 for no debugging whatsoever
caryclark@google.comd88e0892012-03-27 13:23:51 +000020const bool gShowDebugf = false; // FIXME: remove once debugging is complete
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000021
22#define DEBUG_DUMP 0
23#define DEBUG_ADD 0
24#define DEBUG_ADD_INTERSECTING_TS 0
25#define DEBUG_ADD_BOTTOM_TS 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000026#define DEBUG_ABOVE_BELOW 0
27#define DEBUG_ACTIVE_LESS_THAN 0
28#define DEBUG_SORT_HORIZONTAL 0
29#define DEBUG_OUT 0
30#define DEBUG_OUT_LESS_THAN 0
31#define DEBUG_ADJUST_COINCIDENT 0
caryclark@google.com752b60e2012-03-22 21:11:17 +000032#define DEBUG_BOTTOM 0
33
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000034#else
caryclark@google.comd88e0892012-03-27 13:23:51 +000035const bool gShowDebugf = true; // FIXME: remove once debugging is complete
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000036
37#define DEBUG_DUMP 01
38#define DEBUG_ADD 01
39#define DEBUG_ADD_INTERSECTING_TS 0
40#define DEBUG_ADD_BOTTOM_TS 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000041#define DEBUG_ABOVE_BELOW 01
caryclark@google.coma5764232012-03-28 16:20:21 +000042#define DEBUG_ACTIVE_LESS_THAN 0
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
caryclark@google.com6680fb12012-02-07 22:10:51 +000051static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
caryclark@google.coma5764232012-03-28 16:20:21 +000052 Intersections& intersections) {
53 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
54 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
55 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
56}
57
58static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
59 Intersections& intersections) {
60 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
61 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
62 return intersect(aQuad, bLine, intersections);
63}
64
65static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
66 Intersections& intersections) {
67 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
68 {a[3].fX, a[3].fY}};
69 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
70 intersections.fUsed = intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
71 return intersections.fUsed;
72}
73
74static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
75 Intersections& intersections) {
76 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
77 const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
78 return intersect(aQuad, bQuad, intersections);
79}
80
81static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
82 Intersections& intersections) {
83 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
84 {a[3].fX, a[3].fY}};
85 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
86 {b[3].fX, b[3].fY}};
87 return intersect(aCubic, bCubic, intersections);
caryclark@google.comc6825902012-02-03 22:07:47 +000088}
89
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000090static int LineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
91 SkScalar y, double aRange[2]) {
caryclark@google.coma5764232012-03-28 16:20:21 +000092 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000093 return horizontalLineIntersect(aLine, left, right, y, aRange);
caryclark@google.comc6825902012-02-03 22:07:47 +000094}
95
caryclark@google.comcd4421d2012-03-01 19:16:31 +000096static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.coma5764232012-03-28 16:20:21 +000097 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.comcd4421d2012-03-01 19:16:31 +000098 double x, y;
caryclark@google.coma5764232012-03-28 16:20:21 +000099 xy_at_t(line, t, x, y);
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000100 out->fX = SkDoubleToScalar(x);
101 out->fY = SkDoubleToScalar(y);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000102}
103
caryclark@google.coma5764232012-03-28 16:20:21 +0000104static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
105 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
106 double x, y;
107 xy_at_t(quad, t, x, y);
108 out->fX = SkDoubleToScalar(x);
109 out->fY = SkDoubleToScalar(y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000110}
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000111
caryclark@google.coma5764232012-03-28 16:20:21 +0000112static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
113 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
114 {a[3].fX, a[3].fY}};
115 double x, y;
116 xy_at_t(cubic, t, x, y);
117 out->fX = SkDoubleToScalar(x);
118 out->fY = SkDoubleToScalar(y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000119}
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000120
caryclark@google.com6680fb12012-02-07 22:10:51 +0000121static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000122 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com6680fb12012-02-07 22:10:51 +0000123 double y;
124 xy_at_t(aLine, t, *(double*) 0, y);
125 return SkDoubleToScalar(y);
126}
127
caryclark@google.coma5764232012-03-28 16:20:21 +0000128static SkScalar QuadYAtT(const SkPoint a[3], double t) {
129 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
130 double y;
131 xy_at_t(quad, t, *(double*) 0, y);
132 return SkDoubleToScalar(y);
133}
134
135static SkScalar CubicYAtT(const SkPoint a[4], double t) {
136 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
137 {a[3].fX, a[3].fY}};
138 double y;
139 xy_at_t(cubic, t, *(double*) 0, y);
140 return SkDoubleToScalar(y);
141}
142
caryclark@google.com6680fb12012-02-07 22:10:51 +0000143static void LineSubDivide(const SkPoint a[2], double startT, double endT,
144 SkPoint sub[2]) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000145 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com6680fb12012-02-07 22:10:51 +0000146 _Line dst;
147 sub_divide(aLine, startT, endT, dst);
148 sub[0].fX = SkDoubleToScalar(dst[0].x);
149 sub[0].fY = SkDoubleToScalar(dst[0].y);
150 sub[1].fX = SkDoubleToScalar(dst[1].x);
151 sub[1].fY = SkDoubleToScalar(dst[1].y);
152}
153
caryclark@google.coma5764232012-03-28 16:20:21 +0000154static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
155 SkPoint sub[3]) {
156 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
157 Quadratic dst;
158 sub_divide(aQuad, startT, endT, dst);
159 sub[0].fX = SkDoubleToScalar(dst[0].x);
160 sub[0].fY = SkDoubleToScalar(dst[0].y);
161 sub[1].fX = SkDoubleToScalar(dst[1].x);
162 sub[1].fY = SkDoubleToScalar(dst[1].y);
163 sub[2].fX = SkDoubleToScalar(dst[2].x);
164 sub[2].fY = SkDoubleToScalar(dst[2].y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000165}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000166
caryclark@google.coma5764232012-03-28 16:20:21 +0000167static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
168 SkPoint sub[4]) {
169 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
170 {a[3].fX, a[3].fY}};
171 Cubic dst;
172 sub_divide(aCubic, startT, endT, dst);
173 sub[0].fX = SkDoubleToScalar(dst[0].x);
174 sub[0].fY = SkDoubleToScalar(dst[0].y);
175 sub[1].fX = SkDoubleToScalar(dst[1].x);
176 sub[1].fY = SkDoubleToScalar(dst[1].y);
177 sub[2].fX = SkDoubleToScalar(dst[2].x);
178 sub[2].fY = SkDoubleToScalar(dst[2].y);
179 sub[3].fX = SkDoubleToScalar(dst[3].x);
180 sub[3].fY = SkDoubleToScalar(dst[3].y);
181}
182
caryclark@google.comc6825902012-02-03 22:07:47 +0000183/*
184list of edges
185bounds for edge
186sort
187active T
188
189if a contour's bounds is outside of the active area, no need to create edges
190*/
191
192/* given one or more paths,
193 find the bounds of each contour, select the active contours
194 for each active contour, compute a set of edges
195 each edge corresponds to one or more lines and curves
196 leave edges unbroken as long as possible
197 when breaking edges, compute the t at the break but leave the control points alone
198
199 */
200
201void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray) {
202 SkPath::Iter iter(path, false);
203 SkPoint pts[4];
204 SkPath::Verb verb;
205 SkRect bounds;
206 bounds.setEmpty();
207 int count = 0;
208 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
209 switch (verb) {
210 case SkPath::kMove_Verb:
211 if (!bounds.isEmpty()) {
212 *boundsArray.append() = bounds;
213 }
214 bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
215 count = 0;
216 break;
217 case SkPath::kLine_Verb:
218 count = 1;
219 break;
220 case SkPath::kQuad_Verb:
221 count = 2;
222 break;
223 case SkPath::kCubic_Verb:
224 count = 3;
225 break;
226 case SkPath::kClose_Verb:
227 count = 0;
228 break;
229 default:
230 SkDEBUGFAIL("bad verb");
231 return;
232 }
233 for (int i = 1; i <= count; ++i) {
234 bounds.growToInclude(pts[i].fX, pts[i].fY);
235 }
236 }
237}
238
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000239static bool extendLine(const SkPoint line[2], const SkPoint& add) {
240 // FIXME: allow this to extend lines that have slopes that are nearly equal
241 SkScalar dx1 = line[1].fX - line[0].fX;
242 SkScalar dy1 = line[1].fY - line[0].fY;
243 SkScalar dx2 = add.fX - line[0].fX;
244 SkScalar dy2 = add.fY - line[0].fY;
245 return dx1 * dy2 == dx2 * dy1;
246}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000247
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000248// OPTIMIZATION: this should point to a list of input data rather than duplicating
249// the line data here. This would reduce the need to assemble the results.
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000250struct OutEdge {
251 bool operator<(const OutEdge& rh) const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000252 const SkPoint& first = fPts[0];
253 const SkPoint& rhFirst = rh.fPts[0];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000254 return first.fY == rhFirst.fY
255 ? first.fX < rhFirst.fX
256 : first.fY < rhFirst.fY;
257 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000258
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000259 SkPoint fPts[4];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000260 int fID; // id of edge generating data
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000261 uint8_t fVerb; // FIXME: not read from everywhere
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000262 bool fCloseCall; // edge is trimmable if not originally coincident
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000263};
264
caryclark@google.com6680fb12012-02-07 22:10:51 +0000265class OutEdgeBuilder {
266public:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000267 OutEdgeBuilder(bool fill)
268 : fFill(fill) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000269 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000270
caryclark@google.coma5764232012-03-28 16:20:21 +0000271 void addCurve(const SkPoint line[4], SkPath::Verb verb, int id,
272 bool closeCall) {
caryclark@google.comc17972e2012-02-20 21:33:22 +0000273 OutEdge& newEdge = fEdges.push_back();
caryclark@google.coma5764232012-03-28 16:20:21 +0000274 memcpy(newEdge.fPts, line, (verb + 1) * sizeof(SkPoint));
275 newEdge.fVerb = verb;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000276 newEdge.fID = id;
277 newEdge.fCloseCall = closeCall;
278 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000279
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000280 bool trimLine(SkScalar y, int id) {
281 size_t count = fEdges.count();
282 while (count-- != 0) {
283 OutEdge& edge = fEdges[count];
284 if (edge.fID != id) {
285 continue;
286 }
287 if (edge.fCloseCall) {
288 return false;
289 }
290 SkASSERT(edge.fPts[0].fY <= y);
291 if (edge.fPts[1].fY <= y) {
292 continue;
293 }
294 edge.fPts[1].fX = edge.fPts[0].fX + (y - edge.fPts[0].fY)
295 * (edge.fPts[1].fX - edge.fPts[0].fX)
296 / (edge.fPts[1].fY - edge.fPts[0].fY);
297 edge.fPts[1].fY = y;
298 if (gShowDebugf) {
299 SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id,
300 edge.fPts[1].fX, y);
301 }
302 return true;
303 }
304 return false;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000305 }
306
307 void assemble(SkPath& simple) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000308 size_t listCount = fEdges.count();
309 if (listCount == 0) {
310 return;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000311 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000312 do {
313 size_t listIndex = 0;
314 int advance = 1;
315 while (listIndex < listCount && fTops[listIndex] == 0) {
316 ++listIndex;
317 }
318 if (listIndex >= listCount) {
319 break;
320 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000321 int closeEdgeIndex = -listIndex - 1;
caryclark@google.coma5764232012-03-28 16:20:21 +0000322 SkPoint firstPt, lastCurve[4];
323 uint8_t lastVerb;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000324 bool doMove = true;
325 int edgeIndex;
326 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000327 SkPoint* ptArray = fEdges[listIndex].fPts;
328 uint8_t verb = fEdges[listIndex].fVerb;
329 SkPoint* start, * end;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000330 if (advance < 0) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000331 start = &ptArray[verb];
332 end = &ptArray[0];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000333 } else {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000334 start = &ptArray[0];
335 end = &ptArray[verb];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000336 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000337 if (doMove) {
338 firstPt = *start;
339 simple.moveTo(start->fX, start->fY);
340 if (gShowDebugf) {
341 SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
342 start->fX, start->fY);
343 }
344 lastCurve[0] = *start;
345 if (verb == SkPath::kQuad_Verb) {
346 lastCurve[1] = ptArray[1];
347 } else if (verb == SkPath::kCubic_Verb) {
348 if (advance < 0) {
349 lastCurve[1] = ptArray[2];
350 lastCurve[2] = ptArray[1];
351 } else {
352 lastCurve[1] = ptArray[1];
353 lastCurve[2] = ptArray[2];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000354 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000355 }
356 lastCurve[verb] = *end;
357 lastVerb = verb;
358 doMove = false;
359 } else {
360 bool gap = lastCurve[verb] != *start;
361 if (gap) {
362 // FIXME: see comment in bridge -- this probably
363 // conceals errors
364 SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY, start->fY) <= 10);
365 switch (lastVerb) {
366 case SkPath::kLine_Verb:
367 simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
368 break;
369 case SkPath::kQuad_Verb:
370 simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
371 lastCurve[2].fX, lastCurve[2].fY);
372 break;
373 case SkPath::kCubic_Verb:
374 simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
375 lastCurve[2].fX, lastCurve[2].fY,
376 lastCurve[3].fX, lastCurve[3].fY);
377 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000378 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000379 if (gShowDebugf) {
380 const char* verbStr[] = {"", "line", "quad", "cubic"};
381 SkDebugf("%s %sTo-1 (%g,%g)\n", __FUNCTION__,
382 verbStr[lastVerb], lastCurve[lastVerb].fX,
383 lastCurve[lastVerb].fY);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000384 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000385 }
386 if (gap || lastVerb != SkPath::kLine_Verb || !extendLine(lastCurve, *end)) {
387 // FIXME: see comment in bridge -- this probably
388 // conceals errors
389 SkASSERT(lastCurve[lastVerb] == *start ||
390 (fFill && UlpsDiff(lastCurve[lastVerb].fY, start->fY) <= 10));
391 simple.lineTo(start->fX, start->fY);
392 if (gShowDebugf) {
393 SkDebugf("%s lineTo (%g,%g)\n", __FUNCTION__,
394 start->fX, start->fY);
395 }
396 lastCurve[0] = *start;
397 }
398 if (verb == SkPath::kQuad_Verb) {
399 lastCurve[1] = ptArray[1];
400 } else if (verb == SkPath::kCubic_Verb) {
401 if (advance < 0) {
402 lastCurve[1] = ptArray[2];
403 lastCurve[2] = ptArray[1];
404 } else {
405 lastCurve[1] = ptArray[1];
406 lastCurve[2] = ptArray[2];
407 }
408 }
409 lastCurve[verb] = *end;
410 lastVerb = verb;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000411 }
412 if (advance < 0) {
413 edgeIndex = fTops[listIndex];
414 fTops[listIndex] = 0;
415 } else {
416 edgeIndex = fBottoms[listIndex];
417 fBottoms[listIndex] = 0;
418 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000419 if (edgeIndex) {
420 listIndex = abs(edgeIndex) - 1;
421 if (edgeIndex < 0) {
422 fTops[listIndex] = 0;
423 } else {
424 fBottoms[listIndex] = 0;
425 }
426 }
427 if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000428 if (lastCurve[lastVerb] != firstPt) {
429 switch (lastVerb) {
430 case SkPath::kLine_Verb:
431 simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
432 break;
433 case SkPath::kQuad_Verb:
434 simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
435 lastCurve[2].fX, lastCurve[2].fY);
436 break;
437 case SkPath::kCubic_Verb:
438 simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
439 lastCurve[2].fX, lastCurve[2].fY,
440 lastCurve[3].fX, lastCurve[3].fY);
441 break;
442 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000443 if (gShowDebugf) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000444 const char* verbStr[] = {"", "line", "quad", "cubic"};
445 SkDebugf("%s %sTo last (%g, %g)\n", __FUNCTION__,
446 verbStr[lastVerb],
447 lastCurve[lastVerb].fX, lastCurve[lastVerb].fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000448 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000449 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000450 simple.lineTo(firstPt.fX, firstPt.fY);
451 simple.close();
452 if (gShowDebugf) {
caryclark@google.com4917f172012-03-05 22:01:21 +0000453 SkDebugf("%s close (%g, %g)\n", __FUNCTION__,
454 firstPt.fX, firstPt.fY);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000455 }
456 break;
457 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000458 // if this and next edge go different directions
459 if (advance > 0 ^ edgeIndex < 0) {
460 advance = -advance;
461 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000462 } while (edgeIndex);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000463 } while (true);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000464 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000465
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000466 // sort points by y, then x
467 // if x/y is identical, sort bottoms before tops
468 // if identical and both tops/bottoms, sort by angle
469 static bool lessThan(SkTArray<OutEdge>& edges, const int one,
470 const int two) {
471 const OutEdge& oneEdge = edges[abs(one) - 1];
472 int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
473 const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
474 const OutEdge& twoEdge = edges[abs(two) - 1];
475 int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
476 const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
477 if (startPt1.fY != startPt2.fY) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000478 #if DEBUG_OUT_LESS_THAN
479 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
480 one, two, startPt1.fY, startPt2.fY,
481 startPt1.fY < startPt2.fY ? "true" : "false");
482 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000483 return startPt1.fY < startPt2.fY;
484 }
485 if (startPt1.fX != startPt2.fX) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000486 #if DEBUG_OUT_LESS_THAN
487 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
488 one, two, startPt1.fX, startPt2.fX,
489 startPt1.fX < startPt2.fX ? "true" : "false");
490 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000491 return startPt1.fX < startPt2.fX;
492 }
493 const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
494 const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
495 SkScalar dy1 = startPt1.fY - endPt1.fY;
496 SkScalar dy2 = startPt2.fY - endPt2.fY;
497 SkScalar dy1y2 = dy1 * dy2;
498 if (dy1y2 < 0) { // different signs
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000499 #if DEBUG_OUT_LESS_THAN
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000500 SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
501 dy1 > 0 ? "true" : "false");
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000502 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000503 return dy1 > 0; // one < two if one goes up and two goes down
504 }
505 if (dy1y2 == 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000506 #if DEBUG_OUT_LESS_THAN
507 SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
508 one, two, endPt1.fX < endPt2.fX ? "true" : "false");
509 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000510 return endPt1.fX < endPt2.fX;
511 }
512 SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
513 SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000514 #if DEBUG_OUT_LESS_THAN
515 SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
516 one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
517 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000518 return dy2 > 0 ^ dx1y2 < dx2y1;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000519 }
520
caryclark@google.com6008c6562012-02-15 22:01:16 +0000521 // Sort the indices of paired points and then create more indices so
522 // assemble() can find the next edge and connect the top or bottom
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000523 void bridge() {
524 size_t index;
525 size_t count = fEdges.count();
526 if (!count) {
527 return;
528 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000529 SkASSERT(!fFill || count > 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000530 fTops.setCount(count);
531 sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
532 fBottoms.setCount(count);
533 sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000534 SkTDArray<int> order;
535 for (index = 1; index <= count; ++index) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000536 *order.append() = -index;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000537 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000538 for (index = 1; index <= count; ++index) {
539 *order.append() = index;
540 }
541 QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000542 int* lastPtr = order.end() - 1;
543 int* leftPtr = order.begin();
544 while (leftPtr < lastPtr) {
545 int leftIndex = *leftPtr;
546 int leftOutIndex = abs(leftIndex) - 1;
547 const OutEdge& left = fEdges[leftOutIndex];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000548 int* rightPtr = leftPtr + 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000549 int rightIndex = *rightPtr;
550 int rightOutIndex = abs(rightIndex) - 1;
551 const OutEdge& right = fEdges[rightOutIndex];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000552 bool pairUp = fFill;
553 if (!pairUp) {
554 const SkPoint& leftMatch =
555 left.fPts[leftIndex < 0 ? 0 : left.fVerb];
556 const SkPoint& rightMatch =
557 right.fPts[rightIndex < 0 ? 0 : right.fVerb];
558 pairUp = leftMatch == rightMatch;
559 } else {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000560 #if DEBUG_OUT
caryclark@google.comd88e0892012-03-27 13:23:51 +0000561 // FIXME : not happy that error in low bit is allowed
562 // this probably conceals error elsewhere
563 if (UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
564 right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) > 1) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000565 *fMismatches.append() = leftIndex;
566 if (rightPtr == lastPtr) {
567 *fMismatches.append() = rightIndex;
568 }
569 pairUp = false;
570 }
571 #else
caryclark@google.comd88e0892012-03-27 13:23:51 +0000572 SkASSERT(UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
573 right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) <= 10);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000574 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000575 }
576 if (pairUp) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000577 if (leftIndex < 0) {
578 fTops[leftOutIndex] = rightIndex;
579 } else {
580 fBottoms[leftOutIndex] = rightIndex;
581 }
582 if (rightIndex < 0) {
583 fTops[rightOutIndex] = leftIndex;
584 } else {
585 fBottoms[rightOutIndex] = leftIndex;
586 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000587 ++rightPtr;
588 }
589 leftPtr = rightPtr;
590 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000591#if DEBUG_OUT
592 int* mismatch = fMismatches.begin();
593 while (mismatch != fMismatches.end()) {
594 int leftIndex = *mismatch++;
595 int leftOutIndex = abs(leftIndex) - 1;
596 const OutEdge& left = fEdges[leftOutIndex];
597 const SkPoint& leftPt = left.fPts[leftIndex < 0 ? 0 : left.fVerb];
598 SkDebugf("%s left=%d %s (%1.9g,%1.9g)\n",
599 __FUNCTION__, left.fID, leftIndex < 0 ? "top" : "bot",
600 leftPt.fX, leftPt.fY);
601 }
602 SkASSERT(fMismatches.count() == 0);
603#endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000604 }
605
caryclark@google.com6008c6562012-02-15 22:01:16 +0000606protected:
caryclark@google.com6680fb12012-02-07 22:10:51 +0000607 SkTArray<OutEdge> fEdges;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000608 SkTDArray<int> fTops;
609 SkTDArray<int> fBottoms;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000610 bool fFill;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000611#if DEBUG_OUT
612 SkTDArray<int> fMismatches;
613#endif
caryclark@google.com6680fb12012-02-07 22:10:51 +0000614};
615
caryclark@google.comc6825902012-02-03 22:07:47 +0000616// Bounds, unlike Rect, does not consider a vertical line to be empty.
617struct Bounds : public SkRect {
618 static bool Intersects(const Bounds& a, const Bounds& b) {
619 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
620 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
621 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000622
caryclark@google.com6008c6562012-02-15 22:01:16 +0000623 bool isEmpty() {
624 return fLeft > fRight || fTop > fBottom
625 || fLeft == fRight && fTop == fBottom
626 || isnan(fLeft) || isnan(fRight)
627 || isnan(fTop) || isnan(fBottom);
628 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000629};
630
caryclark@google.com4917f172012-03-05 22:01:21 +0000631class Intercepts {
632public:
633 Intercepts()
634 : fTopIntercepts(0)
635 , fBottomIntercepts(0) {
636 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000637
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000638#if DEBUG_DUMP
caryclark@google.coma5764232012-03-28 16:20:21 +0000639 void dump(const SkPoint* pts, SkPath::Verb verb) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000640 const char className[] = "Intercepts";
641 const int tab = 8;
642 for (int i = 0; i < fTs.count(); ++i) {
643 SkPoint out;
caryclark@google.coma5764232012-03-28 16:20:21 +0000644 switch (verb) {
645 case SkPath::kLine_Verb:
646 LineXYAtT(pts, fTs[i], &out);
647 break;
648 case SkPath::kQuad_Verb:
649 QuadXYAtT(pts, fTs[i], &out);
650 break;
651 case SkPath::kCubic_Verb:
652 CubicXYAtT(pts, fTs[i], &out);
653 break;
654 default:
655 SkASSERT(0);
656 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000657 SkDebugf("%*s.fTs[%d]=%1.9g (%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000658 className, i, fTs[i], out.fX, out.fY);
659 }
660 SkDebugf("%*s.fTopIntercepts=%d\n", tab + sizeof(className),
661 className, fTopIntercepts);
662 SkDebugf("%*s.fBottomIntercepts=%d\n", tab + sizeof(className),
663 className, fBottomIntercepts);
664 }
665#endif
666
caryclark@google.comc6825902012-02-03 22:07:47 +0000667 SkTDArray<double> fTs;
caryclark@google.com4917f172012-03-05 22:01:21 +0000668 int fTopIntercepts;
669 int fBottomIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000670};
671
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000672struct HorizontalEdge {
673 bool operator<(const HorizontalEdge& rh) const {
674 return fY == rh.fY ? fLeft == rh.fLeft ? fRight < rh.fRight
675 : fLeft < rh.fLeft : fY < rh.fY;
676 }
677
678#if DEBUG_DUMP
679 void dump() {
680 const char className[] = "HorizontalEdge";
681 const int tab = 4;
caryclark@google.com752b60e2012-03-22 21:11:17 +0000682 SkDebugf("%*s.fLeft=%1.9g\n", tab + sizeof(className), className, fLeft);
683 SkDebugf("%*s.fRight=%1.9g\n", tab + sizeof(className), className, fRight);
684 SkDebugf("%*s.fY=%1.9g\n", tab + sizeof(className), className, fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000685 }
686#endif
687
688 SkScalar fLeft;
689 SkScalar fRight;
690 SkScalar fY;
691};
692
caryclark@google.com6680fb12012-02-07 22:10:51 +0000693struct InEdge {
694 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000695 return fBounds.fTop == rh.fBounds.fTop
696 ? fBounds.fLeft < rh.fBounds.fLeft
697 : fBounds.fTop < rh.fBounds.fTop;
698 }
699
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000700 // Avoid collapsing t values that are close to the same since
701 // we walk ts to describe consecutive intersections. Since a pair of ts can
702 // be nearly equal, any problems caused by this should be taken care
703 // of later.
704 int add(double* ts, size_t count, ptrdiff_t verbIndex) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000705 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
caryclark@google.com6008c6562012-02-15 22:01:16 +0000706 bool foundIntercept = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000707 int insertedAt = -1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000708 Intercepts& intercepts = fIntercepts[verbIndex];
caryclark@google.comc6825902012-02-03 22:07:47 +0000709 for (size_t index = 0; index < count; ++index) {
710 double t = ts[index];
caryclark@google.com4917f172012-03-05 22:01:21 +0000711 if (t <= 0) {
712 fContainsIntercepts |= ++intercepts.fTopIntercepts > 1;
713 continue;
714 }
715 if (t >= 1) {
716 fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000717 continue;
718 }
719 foundIntercept = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000720 size_t tCount = intercepts.fTs.count();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000721 double delta;
caryclark@google.comc6825902012-02-03 22:07:47 +0000722 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
723 if (t <= intercepts.fTs[idx2]) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000724 // FIXME: ? if (t < intercepts.fTs[idx2]) // failed
725 delta = intercepts.fTs[idx2] - t;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000726 if (delta > 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000727 insertedAt = idx2;
caryclark@google.comc6825902012-02-03 22:07:47 +0000728 *intercepts.fTs.insert(idx2) = t;
caryclark@google.comc6825902012-02-03 22:07:47 +0000729 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000730 goto nextPt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000731 }
732 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000733 if (tCount == 0 || (delta = t - intercepts.fTs[tCount - 1]) > 0) {
734 insertedAt = tCount;
caryclark@google.comc6825902012-02-03 22:07:47 +0000735 *intercepts.fTs.append() = t;
736 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000737 nextPt:
738 ;
caryclark@google.comc6825902012-02-03 22:07:47 +0000739 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000740 fContainsIntercepts |= foundIntercept;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000741 return insertedAt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000742 }
743
caryclark@google.com6680fb12012-02-07 22:10:51 +0000744 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000745 // FIXME: in the pathological case where there is a ton of edges, binary search?
746 size_t count = fCached.count();
747 for (size_t index = 0; index < count; ++index) {
748 if (edge == fCached[index]) {
749 return true;
750 }
751 if (edge < fCached[index]) {
752 *fCached.insert(index) = edge;
753 return false;
754 }
755 }
756 *fCached.append() = edge;
757 return false;
758 }
759
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000760 void complete(signed char winding, int id) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000761 SkPoint* ptPtr = fPts.begin();
762 SkPoint* ptLast = fPts.end();
763 if (ptPtr == ptLast) {
764 SkDebugf("empty edge\n");
765 SkASSERT(0);
766 // FIXME: delete empty edge?
767 return;
768 }
769 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
770 ++ptPtr;
771 while (ptPtr != ptLast) {
772 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
773 ++ptPtr;
774 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000775 fIntercepts.push_back_n(fVerbs.count());
caryclark@google.com6680fb12012-02-07 22:10:51 +0000776 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
777 size_t index;
778 size_t last = fPts.count() - 1;
779 for (index = 0; index < last; ++index, --last) {
780 SkTSwap<SkPoint>(fPts[index], fPts[last]);
781 }
782 last = fVerbs.count() - 1;
783 for (index = 0; index < last; ++index, --last) {
784 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
785 }
786 }
787 fContainsIntercepts = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000788 fID = id;
caryclark@google.comc6825902012-02-03 22:07:47 +0000789 }
790
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000791#if DEBUG_DUMP
792 void dump() {
793 int i;
794 const char className[] = "InEdge";
795 const int tab = 4;
796 SkDebugf("InEdge %p (edge=%d)\n", this, fID);
797 for (i = 0; i < fCached.count(); ++i) {
798 SkDebugf("%*s.fCached[%d]=0x%08x\n", tab + sizeof(className),
799 className, i, fCached[i]);
800 }
801 uint8_t* verbs = fVerbs.begin();
802 SkPoint* pts = fPts.begin();
803 for (i = 0; i < fIntercepts.count(); ++i) {
804 SkDebugf("%*s.fIntercepts[%d]:\n", tab + sizeof(className),
805 className, i);
806 // FIXME: take current verb into consideration
caryclark@google.coma5764232012-03-28 16:20:21 +0000807 fIntercepts[i].dump(pts, (SkPath::Verb) *verbs);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000808 pts += *verbs++;
809 }
810 for (i = 0; i < fPts.count(); ++i) {
caryclark@google.com752b60e2012-03-22 21:11:17 +0000811 SkDebugf("%*s.fPts[%d]=(%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000812 className, i, fPts[i].fX, fPts[i].fY);
813 }
814 for (i = 0; i < fVerbs.count(); ++i) {
815 SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className),
816 className, i, fVerbs[i]);
817 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000818 SkDebugf("%*s.fBounds=(%1.9g. %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000819 className, fBounds.fLeft, fBounds.fTop,
820 fBounds.fRight, fBounds.fBottom);
821 SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className,
822 fWinding);
823 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
824 className, fContainsIntercepts);
825 }
826#endif
827
caryclark@google.comc6825902012-02-03 22:07:47 +0000828 // temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +0000829 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +0000830 SkTArray<Intercepts> fIntercepts; // one per verb
caryclark@google.com4917f172012-03-05 22:01:21 +0000831
caryclark@google.comc6825902012-02-03 22:07:47 +0000832 // persistent data
833 SkTDArray<SkPoint> fPts;
834 SkTDArray<uint8_t> fVerbs;
835 Bounds fBounds;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000836 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +0000837 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000838 bool fContainsIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000839};
840
caryclark@google.com6680fb12012-02-07 22:10:51 +0000841class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +0000842public:
843
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000844InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges,
845 SkTDArray<HorizontalEdge>& horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000846 : fPath(path)
847 , fCurrentEdge(NULL)
848 , fEdges(edges)
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000849 , fID(0)
850 , fHorizontalEdges(horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000851 , fIgnoreHorizontal(ignoreHorizontal)
852{
853 walk();
854}
855
856protected:
857
858void addEdge() {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000859 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +0000860 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
861 fPtOffset = 1;
862 *fCurrentEdge->fVerbs.append() = fVerb;
863}
864
caryclark@google.com6008c6562012-02-15 22:01:16 +0000865bool complete() {
866 if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000867 fCurrentEdge->complete(fWinding, ++fID);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000868 fCurrentEdge = NULL;
869 return true;
870 }
871 return false;
872}
873
caryclark@google.comc6825902012-02-03 22:07:47 +0000874int direction(int count) {
875 fPtCount = count;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000876 if (fIgnoreHorizontal && isHorizontal()) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000877 return 0;
878 }
879 int last = count - 1;
880 return fPts[0].fY == fPts[last].fY
caryclark@google.com6680fb12012-02-07 22:10:51 +0000881 ? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX
882 ? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +0000883}
884
885bool isHorizontal() {
886 SkScalar y = fPts[0].fY;
887 for (int i = 1; i < fPtCount; ++i) {
888 if (fPts[i].fY != y) {
889 return false;
890 }
891 }
892 return true;
893}
894
895void startEdge() {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000896 if (!fCurrentEdge) {
897 fCurrentEdge = fEdges.push_back_n(1);
898 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000899 fWinding = 0;
900 fPtOffset = 0;
901}
902
903void walk() {
904 SkPath::Iter iter(fPath, true);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000905 int winding = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +0000906 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
907 switch (fVerb) {
908 case SkPath::kMove_Verb:
caryclark@google.comc6825902012-02-03 22:07:47 +0000909 startEdge();
910 continue;
911 case SkPath::kLine_Verb:
912 winding = direction(2);
913 break;
914 case SkPath::kQuad_Verb:
915 winding = direction(3);
916 break;
917 case SkPath::kCubic_Verb:
918 winding = direction(4);
919 break;
920 case SkPath::kClose_Verb:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000921 SkASSERT(fCurrentEdge);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000922 complete();
caryclark@google.comc6825902012-02-03 22:07:47 +0000923 continue;
924 default:
925 SkDEBUGFAIL("bad verb");
926 return;
927 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000928 if (winding == 0) {
929 HorizontalEdge* horizontalEdge = fHorizontalEdges.append();
930 // FIXME: for degenerate quads and cubics, compute x extremes
931 horizontalEdge->fLeft = fPts[0].fX;
932 horizontalEdge->fRight = fPts[fVerb].fX;
933 horizontalEdge->fY = fPts[0].fY;
934 if (horizontalEdge->fLeft > horizontalEdge->fRight) {
935 SkTSwap<SkScalar>(horizontalEdge->fLeft, horizontalEdge->fRight);
936 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000937 if (complete()) {
938 startEdge();
939 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000940 continue;
941 }
942 if (fWinding + winding == 0) {
943 // FIXME: if prior verb or this verb is a horizontal line, reverse
944 // it instead of starting a new edge
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000945 SkASSERT(fCurrentEdge);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000946 if (complete()) {
947 startEdge();
948 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000949 }
950 fWinding = winding;
951 addEdge();
952 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +0000953 if (!complete()) {
954 if (fCurrentEdge) {
955 fEdges.pop_back();
956 }
957 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000958}
959
960private:
961 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000962 InEdge* fCurrentEdge;
963 SkTArray<InEdge>& fEdges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000964 SkTDArray<HorizontalEdge>& fHorizontalEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +0000965 SkPoint fPts[4];
966 SkPath::Verb fVerb;
967 int fPtCount;
968 int fPtOffset;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000969 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +0000970 int8_t fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000971 bool fIgnoreHorizontal;
972};
973
caryclark@google.com6680fb12012-02-07 22:10:51 +0000974struct WorkEdge {
975 SkScalar bottom() const {
976 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +0000977 }
978
caryclark@google.com6680fb12012-02-07 22:10:51 +0000979 void init(const InEdge* edge) {
980 fEdge = edge;
981 fPts = edge->fPts.begin();
982 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +0000983 }
984
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000985 bool advance() {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000986 SkASSERT(fVerb < fEdge->fVerbs.end());
987 fPts += *fVerb++;
988 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +0000989 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000990
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000991 SkPath::Verb lastVerb() const {
992 SkASSERT(fVerb > fEdge->fVerbs.begin());
993 return (SkPath::Verb) fVerb[-1];
994 }
995
caryclark@google.comc6825902012-02-03 22:07:47 +0000996
997 SkPath::Verb verb() const {
998 return (SkPath::Verb) *fVerb;
999 }
1000
caryclark@google.com6008c6562012-02-15 22:01:16 +00001001 ptrdiff_t verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001002 return fVerb - fEdge->fVerbs.begin();
1003 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001004
caryclark@google.com6680fb12012-02-07 22:10:51 +00001005 int winding() const {
1006 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +00001007 }
1008
caryclark@google.com6680fb12012-02-07 22:10:51 +00001009 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +00001010 const SkPoint* fPts;
1011 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +00001012};
1013
caryclark@google.com6680fb12012-02-07 22:10:51 +00001014// always constructed with SkTDArray because new edges are inserted
1015// this may be a inappropriate optimization, suggesting that a separate array of
1016// ActiveEdge* may be faster to insert and search
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001017
1018// OPTIMIZATION: Brian suggests that global sorting should be unnecessary, since
1019// as active edges are introduced, only local sorting should be required
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001020class ActiveEdge {
1021public:
caryclark@google.com6008c6562012-02-15 22:01:16 +00001022 bool operator<(const ActiveEdge& rh) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001023 double topD = fAbove.fX - rh.fAbove.fX;
1024 if (rh.fAbove.fY < fAbove.fY) {
1025 topD = (rh.fBelow.fY - rh.fAbove.fY) * topD
1026 - (fAbove.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
1027 } else if (rh.fAbove.fY > fAbove.fY) {
1028 topD = (fBelow.fY - fAbove.fY) * topD
1029 + (rh.fAbove.fY - fAbove.fY) * (fBelow.fX - fAbove.fX);
1030 }
1031 double botD = fBelow.fX - rh.fBelow.fX;
1032 if (rh.fBelow.fY > fBelow.fY) {
1033 botD = (rh.fBelow.fY - rh.fAbove.fY) * botD
1034 - (fBelow.fY - rh.fBelow.fY) * (rh.fBelow.fX - rh.fAbove.fX);
1035 } else if (rh.fBelow.fY < fBelow.fY) {
1036 botD = (fBelow.fY - fAbove.fY) * botD
1037 + (rh.fBelow.fY - fBelow.fY) * (fBelow.fX - fAbove.fX);
1038 }
1039 // return sign of greater absolute value
1040 return (fabs(topD) > fabs(botD) ? topD : botD) < 0;
1041 }
1042
1043 // OPTIMIZATION: fold return statements into one
1044 bool operator_less_than(const ActiveEdge& rh) const {
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001045 if (rh.fAbove.fY - fAbove.fY > fBelow.fY - rh.fAbove.fY
1046 && fBelow.fY < rh.fBelow.fY
1047 || fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - fAbove.fY
1048 && rh.fBelow.fY < fBelow.fY) {
1049 // FIXME: need to compute distance, not check for points equal
1050 const SkPoint& check = rh.fBelow.fY <= fBelow.fY
1051 && fBelow != rh.fBelow ? rh.fBelow :
1052 rh.fAbove;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001053 #if DEBUG_ACTIVE_LESS_THAN
1054 SkDebugf("%s 1 %c %cthis (edge=%d) {%g,%g %g,%g}"
1055 " < rh (edge=%d) {%g,%g %g,%g} upls=(%d)\n", __FUNCTION__,
1056 rh.fBelow.fY <= fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
1057 (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
1058 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX) ? ' '
1059 : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
1060 rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
1061 UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
1062 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX)));
1063 #endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001064 return (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
1065 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX);
1066 }
1067 // FIXME: need to compute distance, not check for points equal
1068 const SkPoint& check = fBelow.fY <= rh.fBelow.fY
1069 && fBelow != rh.fBelow ? fBelow : fAbove;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001070 #if DEBUG_ACTIVE_LESS_THAN
1071 SkDebugf("%s 2 %c %cthis (edge=%d) {%g,%g %g,%g}"
1072 " < rh (edge=%d) {%g,%g %g,%g} upls=(%d) (%d,%d)\n", __FUNCTION__,
1073 fBelow.fY <= rh.fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
1074 (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
1075 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)
1076 ? ' ' : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
1077 rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
1078 UlpsDiff((rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX),
1079 (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)),
1080 UlpsDiff(fBelow.fX, rh.fBelow.fX), UlpsDiff(fBelow.fY, rh.fBelow.fY));
1081 #endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001082 return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
1083 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001084 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001085
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001086 // If a pair of edges are nearly coincident for some span, add a T in the
1087 // edge so it can be shortened to match the other edge. Note that another
1088 // approach is to trim the edge after it is added to the OutBuilder list --
1089 // FIXME: since this has no effect if the edge is already done (i.e.,
1090 // fYBottom >= y) maybe this can only be done by calling trimLine later.
1091 void addTatYBelow(SkScalar y) {
1092 if (fBelow.fY <= y || fYBottom >= y) {
1093 return;
1094 }
1095 addTatYInner(y);
1096 fFixBelow = true;
1097 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001098
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001099 void addTatYAbove(SkScalar y) {
1100 if (fBelow.fY <= y) {
1101 return;
1102 }
1103 addTatYInner(y);
1104 }
1105
1106 void addTatYInner(SkScalar y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001107 if (fWorkEdge.fPts[0].fY > y) {
1108 backup(y);
1109 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001110 SkScalar left = fWorkEdge.fPts[0].fX;
1111 SkScalar right = fWorkEdge.fPts[1].fX;
1112 if (left > right) {
1113 SkTSwap(left, right);
1114 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001115 double ts[2];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001116 int pts = LineIntersect(fWorkEdge.fPts, left, right, y, ts);
1117 SkASSERT(pts == 1);
1118 // An ActiveEdge or WorkEdge has no need to modify the T values computed
1119 // in the InEdge, except in the following case. If a pair of edges are
1120 // nearly coincident, this may not be detected when the edges are
1121 // intersected. Later, when sorted, and this near-coincidence is found,
1122 // an additional t value must be added, requiring the cast below.
1123 InEdge* writable = const_cast<InEdge*>(fWorkEdge.fEdge);
1124 int insertedAt = writable->add(ts, pts, fWorkEdge.verbIndex());
caryclark@google.com752b60e2012-03-22 21:11:17 +00001125 #if DEBUG_ADJUST_COINCIDENT
1126 SkDebugf("%s edge=%d y=%1.9g t=%1.9g\n", __FUNCTION__, ID(), y, ts[0]);
1127 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001128 if (insertedAt >= 0) {
1129 if (insertedAt + 1 < fTIndex) {
1130 SkASSERT(insertedAt + 2 == fTIndex);
1131 --fTIndex;
1132 }
1133 }
1134 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001135
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001136 bool advanceT() {
1137 SkASSERT(fTIndex <= fTs->count());
1138 return ++fTIndex <= fTs->count();
1139 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001140
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001141 bool advance() {
1142 // FIXME: flip sense of next
1143 bool result = fWorkEdge.advance();
1144 fDone = !result;
1145 initT();
1146 return result;
1147 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001148
1149 void backup(SkScalar y) {
1150 do {
1151 SkASSERT(fWorkEdge.fEdge->fVerbs.begin() < fWorkEdge.fVerb);
1152 fWorkEdge.fPts -= *--fWorkEdge.fVerb;
1153 SkASSERT(fWorkEdge.fEdge->fPts.begin() <= fWorkEdge.fPts);
1154 } while (fWorkEdge.fPts[0].fY >= y);
1155 initT();
1156 fTIndex = fTs->count() + 1;
1157 }
1158
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001159 void calcLeft(SkScalar y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001160 // OPTIMIZE: put a kDone_Verb at the end of the verb list?
caryclark@google.com4917f172012-03-05 22:01:21 +00001161 if (fDone || fBelow.fY > y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001162 return; // nothing to do; use last
caryclark@google.com4917f172012-03-05 22:01:21 +00001163 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001164 calcLeft();
caryclark@google.com752b60e2012-03-22 21:11:17 +00001165 if (fAbove.fY == fBelow.fY) {
1166 SkDebugf("%s edge=%d fAbove.fY != fBelow.fY %1.9g\n", __FUNCTION__,
1167 ID(), fAbove.fY);
1168 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001169 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001170
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001171 void calcLeft() {
caryclark@google.coma5764232012-03-28 16:20:21 +00001172 void (*xyAtTFunc)(const SkPoint a[], double t, SkPoint* out);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001173 switch (fWorkEdge.verb()) {
caryclark@google.coma5764232012-03-28 16:20:21 +00001174 case SkPath::kLine_Verb:
1175 xyAtTFunc = LineXYAtT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001176 break;
caryclark@google.coma5764232012-03-28 16:20:21 +00001177 case SkPath::kQuad_Verb:
1178 xyAtTFunc = QuadXYAtT;
1179 break;
1180 case SkPath::kCubic_Verb:
1181 xyAtTFunc = CubicXYAtT;
1182 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001183 default:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001184 SkASSERT(0);
1185 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001186 // OPTIMIZATION: if fXAbove, fXBelow have already been computed
1187 // for the fTIndex, don't do it again
1188 // For identical x, this lets us know which edge is first.
1189 // If both edges have T values < 1, check x at next T (fXBelow).
1190 int add = (fTIndex <= fTs->count()) - 1;
1191 double tAbove = t(fTIndex + add);
1192 (*xyAtTFunc)(fWorkEdge.fPts, tAbove, &fAbove);
1193 double tBelow = t(fTIndex - ~add);
1194 (*xyAtTFunc)(fWorkEdge.fPts, tBelow, &fBelow);
1195 SkASSERT(tAbove != tBelow);
1196 // FIXME: this can loop forever
1197 // need a break if we hit the end
1198 while (fAbove.fY == fBelow.fY) {
1199 if (add < 0 || fTIndex == fTs->count()) {
1200 add -= 1;
1201 SkASSERT(fTIndex + add >= 0);
1202 tAbove = t(fTIndex + add);
1203 (*xyAtTFunc)(fWorkEdge.fPts, tAbove, &fAbove);
1204 } else {
1205 add += 1;
1206 SkASSERT(fTIndex - ~add <= fTs->count() + 1);
1207 tBelow = t(fTIndex - ~add);
1208 (*xyAtTFunc)(fWorkEdge.fPts, tBelow, &fBelow);
1209 }
1210 }
1211 #if DEBUG_ABOVE_BELOW
1212 fTAbove = tAbove;
1213 fTBelow = tBelow;
1214 #endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00001215 }
1216
caryclark@google.com752b60e2012-03-22 21:11:17 +00001217 bool done(SkScalar bottom) const {
1218 return fDone || fYBottom >= bottom;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001219 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001220
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001221 void fixBelow() {
1222 if (fFixBelow) {
caryclark@google.coma5764232012-03-28 16:20:21 +00001223 switch (fWorkEdge.verb()) {
1224 case SkPath::kLine_Verb:
1225 LineXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1226 break;
1227 case SkPath::kQuad_Verb:
1228 QuadXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1229 break;
1230 case SkPath::kCubic_Verb:
1231 CubicXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1232 break;
1233 default:
1234 SkASSERT(0);
1235 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001236 fFixBelow = false;
1237 }
1238 }
1239
caryclark@google.com6680fb12012-02-07 22:10:51 +00001240 void init(const InEdge* edge) {
1241 fWorkEdge.init(edge);
1242 initT();
caryclark@google.com4917f172012-03-05 22:01:21 +00001243 fBelow.fY = SK_ScalarMin;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001244 fDone = false;
1245 fYBottom = SK_ScalarMin;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001246 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001247
caryclark@google.com6680fb12012-02-07 22:10:51 +00001248 void initT() {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001249 const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
1250 SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
1251 const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
1252 fTs = &interceptPtr->fTs;
1253 // the above is conceptually the same as
1254 // fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
1255 // but templated arrays don't allow returning a pointer to the end() element
caryclark@google.com6680fb12012-02-07 22:10:51 +00001256 fTIndex = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +00001257 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001258
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001259 // OPTIMIZATION: record if two edges are coincident when the are intersected
1260 // It's unclear how to do this -- seems more complicated than recording the
1261 // t values, since the same t values could exist intersecting non-coincident
1262 // edges.
1263 bool isCoincidentWith(const ActiveEdge* edge, SkScalar y) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001264 if (fAbove != edge->fAbove || fBelow != edge->fBelow) {
1265 return false;
1266 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001267 uint8_t verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
1268 uint8_t edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb()
1269 : edge->fWorkEdge.verb();
1270 if (verb != edgeVerb) {
1271 return false;
1272 }
1273 switch (verb) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001274 case SkPath::kLine_Verb: {
caryclark@google.com4917f172012-03-05 22:01:21 +00001275 return true;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001276 }
1277 default:
1278 // FIXME: add support for all curve types
1279 SkASSERT(0);
1280 }
1281 return false;
1282 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001283
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001284 // The shortest close call edge should be moved into a position where
1285 // it contributes if the winding is transitioning to or from zero.
1286 bool swapClose(const ActiveEdge* next, int prev, int wind, int mask) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001287#if DEBUG_ADJUST_COINCIDENT
1288 SkDebugf("%s edge=%d (%g) next=%d (%g) prev=%d wind=%d nextWind=%d\n",
1289 __FUNCTION__, ID(), fBelow.fY, next->ID(), next->fBelow.fY,
1290 prev, wind, wind + next->fWorkEdge.winding());
1291#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001292 if ((prev & mask) == 0 || (wind & mask) == 0) {
1293 return next->fBelow.fY < fBelow.fY;
1294 }
1295 int nextWinding = wind + next->fWorkEdge.winding();
1296 if ((nextWinding & mask) == 0) {
1297 return fBelow.fY < next->fBelow.fY;
1298 }
1299 return false;
1300 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001301
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001302 bool swapCoincident(const ActiveEdge* edge, SkScalar bottom) const {
1303 if (fBelow.fY >= bottom || fDone || edge->fDone) {
1304 return false;
1305 }
1306 ActiveEdge thisWork = *this;
1307 ActiveEdge edgeWork = *edge;
1308 while ((thisWork.advanceT() || thisWork.advance())
1309 && (edgeWork.advanceT() || edgeWork.advance())) {
1310 thisWork.calcLeft();
1311 edgeWork.calcLeft();
1312 if (thisWork < edgeWork) {
1313 return false;
1314 }
1315 if (edgeWork < thisWork) {
1316 return true;
1317 }
1318 }
1319 return false;
1320 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001321
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001322 bool tooCloseToCall(const ActiveEdge* edge) const {
1323 int ulps;
caryclark@google.comd88e0892012-03-27 13:23:51 +00001324 // FIXME: the first variation works better (or at least causes fewer tests
1325 // to fail than the second, although the second's logic better matches the
1326 // current sort criteria. Need to track down the cause of the crash, and
1327 // see if the second case isn't somehow buggy.
1328#if 01
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001329 // FIXME: don't compare points for equality
1330 // OPTIMIZATION: refactor to make one call to UlpsDiff
1331 if (edge->fAbove.fY - fAbove.fY > fBelow.fY - edge->fAbove.fY
1332 && fBelow.fY < edge->fBelow.fY
1333 || fAbove.fY - edge->fAbove.fY < edge->fBelow.fY - fAbove.fY
1334 && edge->fBelow.fY < fBelow.fY) {
1335 const SkPoint& check = edge->fBelow.fY <= fBelow.fY
1336 && fBelow != edge->fBelow ? edge->fBelow :
1337 edge->fAbove;
1338 ulps = UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
1339 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX));
1340 } else {
1341 const SkPoint& check = fBelow.fY <= edge->fBelow.fY
1342 && fBelow != edge->fBelow ? fBelow : fAbove;
1343 ulps = UlpsDiff((edge->fBelow.fY - edge->fAbove.fY)
1344 * (check.fX - edge->fAbove.fX),
1345 (check.fY - edge->fAbove.fY)
1346 * (edge->fBelow.fX - edge->fAbove.fX));
1347 }
caryclark@google.comd88e0892012-03-27 13:23:51 +00001348#else
1349 double t1, t2, b1, b2;
1350 if (edge->fAbove.fY < fAbove.fY) {
1351 t1 = (edge->fBelow.fY - edge->fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
1352 t2 = (fAbove.fY - edge->fAbove.fY) * (edge->fBelow.fX - edge->fAbove.fX);
1353 } else if (edge->fAbove.fY > fAbove.fY) {
1354 t1 = (fBelow.fY - fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
1355 t2 = (fAbove.fY - edge->fAbove.fY) * (fBelow.fX - fAbove.fX);
1356 } else {
1357 t1 = fAbove.fX;
1358 t2 = edge->fAbove.fX;
1359 }
1360 if (edge->fBelow.fY > fBelow.fY) {
1361 b1 = (edge->fBelow.fY - edge->fAbove.fY) * (fBelow.fX - edge->fBelow.fX);
1362 b2 = (fBelow.fY - edge->fBelow.fY) * (edge->fBelow.fX - edge->fAbove.fX);
1363 } else if (edge->fBelow.fY < fBelow.fY) {
1364 b1 = (fBelow.fY - fAbove.fY) * (fBelow.fX - edge->fBelow.fX);
1365 b2 = (fBelow.fY - edge->fBelow.fY) * (fBelow.fX - fAbove.fX);
1366 } else {
1367 b1 = fBelow.fX;
1368 b2 = edge->fBelow.fX;
1369 }
1370 if (fabs(t1 - t2) > fabs(b1 - b2)) {
1371 ulps = UlpsDiff(t1, t2);
1372 } else {
1373 ulps = UlpsDiff(b1, b2);
1374 }
1375#if DEBUG_ADJUST_COINCIDENT
1376 SkDebugf("%s this=%d edge=%d ulps=%d\n", __FUNCTION__, ID(), edge->ID(),
1377 ulps);
1378#endif
1379#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001380 return ulps >= 0 && ulps <= 32;
1381 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001382
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001383 double nextT() const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001384 SkASSERT(fTIndex <= fTs->count());
1385 return t(fTIndex + 1);
1386 }
caryclark@google.com6680fb12012-02-07 22:10:51 +00001387
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001388 double t() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001389 if (fTIndex == 0) {
1390 return 0;
1391 }
1392 if (fTIndex > fTs->count()) {
1393 return 1;
1394 }
1395 return (*fTs)[fTIndex - 1];
1396 }
1397
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001398 double t(int tIndex) const {
1399 if (tIndex == 0) {
1400 return 0;
1401 }
1402 if (tIndex > fTs->count()) {
1403 return 1;
1404 }
1405 return (*fTs)[tIndex - 1];
1406 }
1407
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001408 // FIXME: debugging only
caryclark@google.com752b60e2012-03-22 21:11:17 +00001409 int ID() const {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001410 return fWorkEdge.fEdge->fID;
1411 }
1412
1413public:
caryclark@google.com6680fb12012-02-07 22:10:51 +00001414 WorkEdge fWorkEdge;
1415 const SkTDArray<double>* fTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001416 SkPoint fAbove;
1417 SkPoint fBelow;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001418#if DEBUG_ABOVE_BELOW
1419 double fTAbove;
1420 double fTBelow;
1421#endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001422 SkScalar fYBottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001423 int fCoincident;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001424 int fTIndex;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001425 bool fSkip; // OPTIMIZATION: use bitfields?
1426 bool fCloseCall;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001427 bool fDone;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001428 bool fFixBelow;
caryclark@google.comc6825902012-02-03 22:07:47 +00001429};
1430
caryclark@google.com6680fb12012-02-07 22:10:51 +00001431static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001432 size_t count = activeEdges.count();
1433 for (size_t index = 0; index < count; ++index) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001434 if (edge == activeEdges[index].fWorkEdge.fEdge) {
1435 return;
1436 }
1437 }
1438 ActiveEdge* active = activeEdges.append();
1439 active->init(edge);
1440}
1441
caryclark@google.com4917f172012-03-05 22:01:21 +00001442// Find any intersections in the range of active edges. A pair of edges, on
1443// either side of another edge, may change the winding contribution for part of
1444// the edge.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001445// Keep horizontal edges just for
caryclark@google.com4917f172012-03-05 22:01:21 +00001446// the purpose of computing when edges change their winding contribution, since
1447// this is essentially computing the horizontal intersection.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001448static void addBottomT(InEdge** currentPtr, InEdge** lastPtr,
1449 HorizontalEdge** horizontal) {
1450 InEdge** testPtr = currentPtr - 1;
1451 HorizontalEdge* horzEdge = *horizontal;
1452 SkScalar left = horzEdge->fLeft;
1453 SkScalar bottom = horzEdge->fY;
1454 while (++testPtr != lastPtr) {
1455 InEdge* test = *testPtr;
1456 if (test->fBounds.fBottom <= bottom || test->fBounds.fRight <= left) {
1457 continue;
1458 }
1459 WorkEdge wt;
1460 wt.init(test);
1461 do {
1462 HorizontalEdge** sorted = horizontal;
1463 horzEdge = *sorted;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001464 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001465 if (wt.verb() == SkPath::kLine_Verb) {
1466 double wtTs[2];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001467 int pts = LineIntersect(wt.fPts, horzEdge->fLeft,
1468 horzEdge->fRight, horzEdge->fY, wtTs);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001469 if (pts) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001470#if DEBUG_ADD_BOTTOM_TS
1471 SkDebugf("%s y=%g wtTs[0]=%g (%g,%g, %g,%g)\n", __FUNCTION__,
1472 horzEdge->fY, wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
1473 wt.fPts[1].fX, wt.fPts[1].fY);
1474 if (pts == 2) {
1475 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1476 }
1477#endif
caryclark@google.com4917f172012-03-05 22:01:21 +00001478 test->add(wtTs, pts, wt.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001479 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001480 } else {
1481 // FIXME: add all curve types
1482 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001483 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001484 horzEdge = *++sorted;
1485 } while (horzEdge->fY == bottom
1486 && horzEdge->fLeft <= test->fBounds.fRight);
1487 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001488 }
1489}
1490
caryclark@google.coma5764232012-03-28 16:20:21 +00001491static void debugShowLineIntersection(int pts, const WorkEdge& wt,
1492 const WorkEdge& wn, const double wtTs[2], const double wnTs[2]) {
1493#if DEBUG_ADD_INTERSECTING_TS
1494 if (!pts) {
1495 return;
1496 }
1497 SkPoint wtOutPt, wnOutPt;
1498 LineXYAtT(wt.fPts, wtTs[0], &wtOutPt);
1499 LineXYAtT(wn.fPts, wnTs[0], &wnOutPt);
1500 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
1501 __FUNCTION__,
1502 wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
1503 wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY,
1504 test->fID, next->fID);
1505 if (pts == 2) {
1506 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1507 }
1508 SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
1509 __FUNCTION__,
1510 wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY,
1511 wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY,
1512 test->fID, next->fID);
1513 if (pts == 2) {
1514 SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
1515 }
1516#endif
1517}
1518
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001519static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001520 InEdge** testPtr = currentPtr - 1;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001521 // FIXME: lastPtr should be past the point of interest, so
1522 // test below should be lastPtr - 2
1523 // that breaks testSimplifyTriangle22, so further investigation is needed
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001524 while (++testPtr != lastPtr - 1) {
1525 InEdge* test = *testPtr;
1526 InEdge** nextPtr = testPtr;
1527 do {
1528 InEdge* next = *++nextPtr;
1529 if (test->cached(next)) {
1530 continue;
1531 }
1532 if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
1533 continue;
1534 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001535 WorkEdge wt, wn;
1536 wt.init(test);
1537 wn.init(next);
1538 do {
caryclark@google.coma5764232012-03-28 16:20:21 +00001539 int pts;
1540 Intersections ts;
1541 bool swap = false;
1542 switch (wt.verb()) {
1543 case SkPath::kLine_Verb:
1544 switch (wn.verb()) {
1545 case SkPath::kLine_Verb: {
1546 pts = LineIntersect(wt.fPts, wn.fPts, ts);
1547 debugShowLineIntersection(pts, wt, wn,
1548 ts.fT[0], ts.fT[1]);
1549 break;
1550 }
1551 case SkPath::kQuad_Verb: {
1552 swap = true;
1553 pts = QuadLineIntersect(wn.fPts, wt.fPts, ts);
1554 break;
1555 }
1556 case SkPath::kCubic_Verb: {
1557 swap = true;
1558 pts = CubicLineIntersect(wn.fPts, wt.fPts, ts);
1559 break;
1560 }
1561 default:
1562 SkASSERT(0);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001563 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001564 break;
1565 case SkPath::kQuad_Verb:
1566 switch (wn.verb()) {
1567 case SkPath::kLine_Verb: {
1568 pts = QuadLineIntersect(wt.fPts, wn.fPts, ts);
1569 break;
1570 }
1571 case SkPath::kQuad_Verb: {
1572 pts = QuadIntersect(wt.fPts, wn.fPts, ts);
1573 break;
1574 }
1575 case SkPath::kCubic_Verb: {
1576 // FIXME: promote quad to cubic
1577 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
1578 break;
1579 }
1580 default:
1581 SkASSERT(0);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001582 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001583 break;
1584 case SkPath::kCubic_Verb:
1585 switch (wn.verb()) {
1586 case SkPath::kLine_Verb: {
1587 pts = CubicLineIntersect(wt.fPts, wn.fPts, ts);
1588 break;
1589 }
1590 case SkPath::kQuad_Verb: {
1591 // FIXME: promote quad to cubic
1592 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
1593 break;
1594 }
1595 case SkPath::kCubic_Verb: {
1596 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
1597 break;
1598 }
1599 default:
1600 SkASSERT(0);
1601 }
1602 break;
1603 default:
1604 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001605 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001606 test->add(ts.fT[swap], pts, wt.verbIndex());
1607 next->add(ts.fT[!swap], pts, wn.verbIndex());
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001608 } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
1609 } while (nextPtr != lastPtr);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001610 }
1611}
1612
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001613static InEdge** advanceEdges(SkTDArray<ActiveEdge>* activeEdges,
caryclark@google.com6008c6562012-02-15 22:01:16 +00001614 InEdge** currentPtr, InEdge** lastPtr, SkScalar y) {
1615 InEdge** testPtr = currentPtr - 1;
1616 while (++testPtr != lastPtr) {
1617 if ((*testPtr)->fBounds.fBottom > y) {
1618 continue;
1619 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001620 if (activeEdges) {
1621 InEdge* test = *testPtr;
1622 ActiveEdge* activePtr = activeEdges->begin() - 1;
1623 ActiveEdge* lastActive = activeEdges->end();
1624 while (++activePtr != lastActive) {
1625 if (activePtr->fWorkEdge.fEdge == test) {
1626 activeEdges->remove(activePtr - activeEdges->begin());
1627 break;
1628 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001629 }
1630 }
1631 if (testPtr == currentPtr) {
1632 ++currentPtr;
1633 }
1634 }
1635 return currentPtr;
1636}
1637
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001638// OPTIMIZE: inline?
1639static HorizontalEdge** advanceHorizontal(HorizontalEdge** edge, SkScalar y) {
1640 while ((*edge)->fY < y) {
1641 ++edge;
1642 }
1643 return edge;
1644}
1645
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001646// compute bottom taking into account any intersected edges
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001647static SkScalar computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
1648 SkScalar y, SkScalar bottom) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001649 ActiveEdge* activePtr = activeEdges.begin() - 1;
1650 ActiveEdge* lastActive = activeEdges.end();
1651 while (++activePtr != lastActive) {
1652 const InEdge* test = activePtr->fWorkEdge.fEdge;
1653 if (!test->fContainsIntercepts) {
1654 continue;
1655 }
1656 WorkEdge wt;
1657 wt.init(test);
1658 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001659 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
caryclark@google.com4917f172012-03-05 22:01:21 +00001660 if (intercepts.fTopIntercepts > 1) {
1661 SkScalar yTop = wt.fPts[0].fY;
1662 if (yTop > y && bottom > yTop) {
1663 bottom = yTop;
1664 }
1665 }
1666 if (intercepts.fBottomIntercepts > 1) {
1667 SkScalar yBottom = wt.fPts[wt.verb()].fY;
1668 if (yBottom > y && bottom > yBottom) {
1669 bottom = yBottom;
1670 }
1671 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001672 const SkTDArray<double>& fTs = intercepts.fTs;
1673 size_t count = fTs.count();
1674 for (size_t index = 0; index < count; ++index) {
caryclark@google.coma5764232012-03-28 16:20:21 +00001675 SkScalar yIntercept;
1676 switch (wt.verb()) {
1677 case SkPath::kLine_Verb: {
1678 yIntercept = LineYAtT(wt.fPts, fTs[index]);
1679 break;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001680 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001681 case SkPath::kQuad_Verb: {
1682 yIntercept = QuadYAtT(wt.fPts, fTs[index]);
1683 break;
1684 }
1685 case SkPath::kCubic_Verb: {
1686 yIntercept = CubicYAtT(wt.fPts, fTs[index]);
1687 break;
1688 }
1689 default:
1690 SkASSERT(0); // should never get here
1691 }
1692 if (yIntercept > y && bottom > yIntercept) {
1693 bottom = yIntercept;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001694 }
1695 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001696 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001697 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001698 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001699}
1700
1701static SkScalar findBottom(InEdge** currentPtr,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001702 InEdge** edgeListEnd, SkTDArray<ActiveEdge>* activeEdges, SkScalar y,
caryclark@google.com6008c6562012-02-15 22:01:16 +00001703 bool asFill, InEdge**& testPtr) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001704 InEdge* current = *currentPtr;
1705 SkScalar bottom = current->fBounds.fBottom;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001706
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001707 // find the list of edges that cross y
caryclark@google.com6008c6562012-02-15 22:01:16 +00001708 InEdge* test = *testPtr;
1709 while (testPtr != edgeListEnd) {
1710 SkScalar testTop = test->fBounds.fTop;
1711 if (bottom <= testTop) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001712 break;
1713 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001714 SkScalar testBottom = test->fBounds.fBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001715 // OPTIMIZATION: Shortening the bottom is only interesting when filling
1716 // and when the edge is to the left of a longer edge. If it's a framing
1717 // edge, or part of the right, it won't effect the longer edges.
caryclark@google.com6008c6562012-02-15 22:01:16 +00001718 if (testTop > y) {
1719 bottom = testTop;
1720 break;
1721 }
1722 if (y < testBottom) {
1723 if (bottom > testBottom) {
1724 bottom = testBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001725 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001726 if (activeEdges) {
1727 addToActive(*activeEdges, test);
1728 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001729 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001730 test = *++testPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001731 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001732 return bottom;
1733}
1734
1735static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
1736 SkTDArray<InEdge*>& edgeList) {
1737 size_t edgeCount = edges.count();
1738 if (edgeCount == 0) {
1739 return;
1740 }
1741 for (size_t index = 0; index < edgeCount; ++index) {
1742 *edgeList.append() = &edges[index];
1743 }
1744 edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1745 *edgeList.append() = &edgeSentinel;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001746 QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001747}
1748
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001749static void makeHorizontalList(SkTDArray<HorizontalEdge>& edges,
1750 HorizontalEdge& edgeSentinel, SkTDArray<HorizontalEdge*>& edgeList) {
1751 size_t edgeCount = edges.count();
1752 if (edgeCount == 0) {
1753 return;
1754 }
1755 for (size_t index = 0; index < edgeCount; ++index) {
1756 *edgeList.append() = &edges[index];
1757 }
1758 edgeSentinel.fLeft = edgeSentinel.fRight = edgeSentinel.fY = SK_ScalarMax;
1759 *edgeList.append() = &edgeSentinel;
1760 QSort<HorizontalEdge>(edgeList.begin(), edgeList.end() - 1);
1761}
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001762
1763static void skipCoincidence(int lastWinding, int winding, int windingMask,
1764 ActiveEdge* activePtr, ActiveEdge* firstCoincident) {
1765 if (((lastWinding & windingMask) == 0) ^ (winding & windingMask) != 0) {
1766 return;
1767 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001768 // FIXME: ? shouldn't this be if (lastWinding & windingMask) ?
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001769 if (lastWinding) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001770#if DEBUG_ADJUST_COINCIDENT
1771 SkDebugf("%s edge=%d 1 set skip=false\n", __FUNCTION__, activePtr->ID());
1772#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001773 activePtr->fSkip = false;
1774 } else {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001775#if DEBUG_ADJUST_COINCIDENT
1776 SkDebugf("%s edge=%d 2 set skip=false\n", __FUNCTION__, firstCoincident->ID());
1777#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001778 firstCoincident->fSkip = false;
1779 }
1780}
1781
caryclark@google.com6008c6562012-02-15 22:01:16 +00001782static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001783 SkTDArray<ActiveEdge*>& edgeList, SkScalar y) {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001784 size_t edgeCount = activeEdges.count();
1785 if (edgeCount == 0) {
1786 return;
1787 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001788#if DEBUG_SORT_HORIZONTAL
caryclark@google.comd88e0892012-03-27 13:23:51 +00001789 const int tab = 3; // FIXME: debugging only
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001790 SkDebugf("%s y=%1.9g\n", __FUNCTION__, y);
1791#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00001792 size_t index;
1793 for (index = 0; index < edgeCount; ++index) {
1794 ActiveEdge& activeEdge = activeEdges[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001795 do {
1796 activeEdge.calcLeft(y);
1797 // skip segments that don't span y
1798 if (activeEdge.fAbove != activeEdge.fBelow) {
1799 break;
1800 }
1801 if (activeEdge.fDone) {
1802#if DEBUG_SORT_HORIZONTAL
1803 SkDebugf("%*s edge=%d done\n", tab, "", activeEdge.ID());
1804#endif
1805 goto nextEdge;
1806 }
1807#if DEBUG_SORT_HORIZONTAL
1808 SkDebugf("%*s edge=%d above==below\n", tab, "", activeEdge.ID());
1809#endif
1810 } while (activeEdge.advanceT() || activeEdge.advance());
1811#if DEBUG_SORT_HORIZONTAL
1812 SkDebugf("%*s edge=%d above=(%1.9g,%1.9g) (%1.9g) below=(%1.9g,%1.9g)"
1813 " (%1.9g)\n", tab, "", activeEdge.ID(),
1814 activeEdge.fAbove.fX, activeEdge.fAbove.fY, activeEdge.fTAbove,
1815 activeEdge.fBelow.fX, activeEdge.fBelow.fY, activeEdge.fTBelow);
1816#endif
1817 activeEdge.fSkip = activeEdge.fCloseCall = activeEdge.fFixBelow = false;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001818 *edgeList.append() = &activeEdge;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001819nextEdge:
1820 ;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001821 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001822 QSort<ActiveEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001823}
1824
1825// remove coincident edges
1826// OPTIMIZE: remove edges? This is tricky because the current logic expects
1827// the winding count to be maintained while skipping coincident edges. In
1828// addition to removing the coincident edges, the remaining edges would need
1829// to have a different winding value, possibly different per intercept span.
1830static SkScalar adjustCoincident(SkTDArray<ActiveEdge*>& edgeList,
1831 int windingMask, SkScalar y, SkScalar bottom, OutEdgeBuilder& outBuilder)
1832{
1833#if DEBUG_ADJUST_COINCIDENT
1834 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
1835#endif
1836 size_t edgeCount = edgeList.count();
1837 if (edgeCount == 0) {
1838 return bottom;
1839 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001840 ActiveEdge* activePtr = edgeList[0];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001841 size_t index;
1842 bool foundCoincident = false;
1843 int firstIndex = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001844 for (index = 1; index < edgeCount; ++index) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001845 ActiveEdge* nextPtr = edgeList[index];
1846 bool closeCall = false;
1847 activePtr->fCoincident = firstIndex;
1848 if (activePtr->isCoincidentWith(nextPtr, y)
1849 || (closeCall = activePtr->tooCloseToCall(nextPtr))) {
1850 activePtr->fSkip = nextPtr->fSkip = foundCoincident = true;
1851 activePtr->fCloseCall = nextPtr->fCloseCall = closeCall;
1852 } else {
1853 firstIndex = index;
1854 }
1855 activePtr = nextPtr;
1856 }
1857 activePtr->fCoincident = firstIndex;
1858 if (!foundCoincident) {
1859 return bottom;
1860 }
1861 int winding = 0;
1862 activePtr = edgeList[0];
1863 for (index = 1; index < edgeCount; ++index) {
1864 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001865 winding += activePtr->fWorkEdge.winding();
caryclark@google.com6008c6562012-02-15 22:01:16 +00001866 ActiveEdge* nextPtr = edgeList[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001867 if (activePtr->fCoincident == nextPtr->fCoincident) {
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001868 // the coincident edges may not have been sorted above -- advance
1869 // the edges and resort if needed
1870 // OPTIMIZE: if sorting is done incrementally as new edges are added
1871 // and not all at once as is done here, fold this test into the
1872 // current less than test.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001873 if (activePtr->fCloseCall ? activePtr->swapClose(nextPtr,
1874 priorWinding, winding, windingMask)
1875 : activePtr->swapCoincident(nextPtr, bottom)) {
caryclark@google.com4917f172012-03-05 22:01:21 +00001876 winding -= activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001877 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
1878 SkTSwap<ActiveEdge*>(activePtr, nextPtr);
caryclark@google.com4917f172012-03-05 22:01:21 +00001879 winding += activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001880 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001881 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001882 activePtr = nextPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001883 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001884 int firstCoincidentWinding = 0;
1885 ActiveEdge* firstCoincident = NULL;
1886 winding = 0;
1887 activePtr = edgeList[0];
1888 for (index = 1; index < edgeCount; ++index) {
1889 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001890 winding += activePtr->fWorkEdge.winding();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001891 ActiveEdge* nextPtr = edgeList[index];
1892 if (activePtr->fCoincident == nextPtr->fCoincident) {
1893 if (!firstCoincident) {
1894 firstCoincident = activePtr;
1895 firstCoincidentWinding = priorWinding;
1896 }
1897 if (activePtr->fCloseCall) {
1898 // If one of the edges has already been added to out as a non
1899 // coincident edge, trim it back to the top of this span
1900 if (outBuilder.trimLine(y, activePtr->ID())) {
1901 activePtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001902 #if DEBUG_ADJUST_COINCIDENT
1903 SkDebugf("%s 1 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
1904 __FUNCTION__, activePtr->ID(), y, activePtr->fYBottom);
1905 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001906 activePtr->fYBottom = y;
1907 }
1908 if (outBuilder.trimLine(y, nextPtr->ID())) {
1909 nextPtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001910 #if DEBUG_ADJUST_COINCIDENT
1911 SkDebugf("%s 2 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
1912 __FUNCTION__, nextPtr->ID(), y, nextPtr->fYBottom);
1913 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001914 nextPtr->fYBottom = y;
1915 }
1916 // add missing t values so edges can be the same length
1917 SkScalar testY = activePtr->fBelow.fY;
1918 nextPtr->addTatYBelow(testY);
1919 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001920 #if DEBUG_ADJUST_COINCIDENT
1921 SkDebugf("%s 3 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
1922 __FUNCTION__, activePtr->ID(), testY, bottom);
1923 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001924 bottom = testY;
1925 }
1926 testY = nextPtr->fBelow.fY;
1927 activePtr->addTatYBelow(testY);
1928 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001929 #if DEBUG_ADJUST_COINCIDENT
1930 SkDebugf("%s 4 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
1931 __FUNCTION__, nextPtr->ID(), testY, bottom);
1932 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001933 bottom = testY;
1934 }
1935 }
1936 } else if (firstCoincident) {
1937 skipCoincidence(firstCoincidentWinding, winding, windingMask,
1938 activePtr, firstCoincident);
1939 firstCoincident = NULL;
1940 }
1941 activePtr = nextPtr;
1942 }
1943 if (firstCoincident) {
1944 winding += activePtr->fWorkEdge.winding();
1945 skipCoincidence(firstCoincidentWinding, winding, windingMask, activePtr,
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001946 firstCoincident);
1947 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001948 // fix up the bottom for close call edges. OPTIMIZATION: maybe this could
1949 // be in the loop above, but moved here since loop above reads fBelow and
1950 // it felt unsafe to write it in that loop
1951 for (index = 0; index < edgeCount; ++index) {
1952 (edgeList[index])->fixBelow();
1953 }
1954 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001955}
1956
1957// stitch edge and t range that satisfies operation
caryclark@google.com6008c6562012-02-15 22:01:16 +00001958static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
caryclark@google.com752b60e2012-03-22 21:11:17 +00001959 SkScalar bottom, int windingMask, bool fill, OutEdgeBuilder& outBuilder) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001960 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001961 ActiveEdge** activeHandle = edgeList.begin() - 1;
1962 ActiveEdge** lastActive = edgeList.end();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001963 const int tab = 7; // FIXME: debugging only
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001964 if (gShowDebugf) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001965 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001966 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001967 while (++activeHandle != lastActive) {
1968 ActiveEdge* activePtr = *activeHandle;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001969 const WorkEdge& wt = activePtr->fWorkEdge;
1970 int lastWinding = winding;
1971 winding += wt.winding();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001972 if (gShowDebugf) {
1973 SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d"
1974#if DEBUG_ABOVE_BELOW
1975 " above=%1.9g below=%1.9g"
1976#endif
1977 "\n",
1978 tab-4, "", activePtr->ID(), lastWinding,
1979 winding, activePtr->fSkip, activePtr->fCloseCall
1980#if DEBUG_ABOVE_BELOW
1981 , activePtr->fTAbove, activePtr->fTBelow
1982#endif
1983 );
1984 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001985 if (activePtr->done(bottom)) {
1986 if (gShowDebugf) {
1987 SkDebugf("%*s fDone=%d || fYBottom=%1.9g >= bottom\n", tab, "",
1988 activePtr->fDone, activePtr->fYBottom);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001989 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001990 continue;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001991 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00001992 int opener = (lastWinding & windingMask) == 0;
1993 bool closer = (winding & windingMask) == 0;
1994 SkASSERT(!opener | !closer);
1995 bool inWinding = opener | closer;
caryclark@google.coma5764232012-03-28 16:20:21 +00001996 SkPoint clippedPts[4];
caryclark@google.com4917f172012-03-05 22:01:21 +00001997 const SkPoint* clipped = NULL;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001998 uint8_t verb = wt.verb();
1999 bool moreToDo, aboveBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002000 do {
2001 double currentT = activePtr->t();
caryclark@google.com6008c6562012-02-15 22:01:16 +00002002 SkASSERT(currentT < 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002003 const SkPoint* points = wt.fPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002004 double nextT;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002005 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002006 nextT = activePtr->nextT();
caryclark@google.coma5764232012-03-28 16:20:21 +00002007 // FIXME: obtuse: want efficient way to say
2008 // !currentT && currentT != 1 || !nextT && nextT != 1
2009 if (currentT * nextT != 0 || currentT + nextT != 1) {
2010 // OPTIMIZATION: if !inWinding, we only need
2011 // clipped[1].fY
2012 switch (verb) {
2013 case SkPath::kLine_Verb:
2014 LineSubDivide(points, currentT, nextT, clippedPts);
2015 break;
2016 case SkPath::kQuad_Verb:
2017 QuadSubDivide(points, currentT, nextT, clippedPts);
2018 break;
2019 case SkPath::kCubic_Verb:
2020 CubicSubDivide(points, currentT, nextT, clippedPts);
2021 break;
2022 default:
2023 SkASSERT(0);
2024 break;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002025 }
caryclark@google.coma5764232012-03-28 16:20:21 +00002026 clipped = clippedPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002027 } else {
caryclark@google.coma5764232012-03-28 16:20:21 +00002028 clipped = points;
2029 }
2030 if (inWinding && !activePtr->fSkip && (fill ? clipped[0].fY
2031 != clipped[verb].fY : clipped[0] != clipped[verb])) {
2032 if (gShowDebugf) {
2033 const char* verbStr[] = {"", "Line", "Quad", "Cubic"};
2034 SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d"
2035 " v=%d t=(%1.9g,%1.9g)\n", tab, "",
2036 verbStr[verb], clipped[0].fX, clipped[0].fY,
2037 clipped[verb].fX, clipped[verb].fY,
2038 activePtr->ID(),
2039 activePtr->fWorkEdge.fVerb
2040 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
2041 currentT, nextT);
2042 }
2043 outBuilder.addCurve(clipped, (SkPath::Verb) verb,
2044 activePtr->fWorkEdge.fEdge->fID,
2045 activePtr->fCloseCall);
2046 } else {
2047 if (gShowDebugf ) {
2048 const char* verbStr[] = {"", "Line", "Quad", "Cubic"};
2049 SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g"
2050 " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
2051 verbStr[verb], clipped[0].fX, clipped[0].fY,
2052 clipped[verb].fX, clipped[verb].fY,
2053 activePtr->ID(),
2054 activePtr->fWorkEdge.fVerb
2055 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
2056 currentT, nextT);
2057 }
2058 }
2059 // by advancing fAbove/fBelow, the next call to sortHorizontal
2060 // will use these values if they're still valid instead of
2061 // recomputing
2062 if (clipped[1].fY > activePtr->fBelow.fY
2063 && bottom >= activePtr->fBelow.fY ) {
2064 activePtr->fAbove = activePtr->fBelow;
2065 activePtr->fBelow = clipped[1];
2066 #if DEBUG_ABOVE_BELOW
2067 activePtr->fTAbove = activePtr->fTBelow;
2068 activePtr->fTBelow = nextT;
2069 #endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002070 }
2071 currentT = nextT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002072 moreToDo = activePtr->advanceT();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002073 activePtr->fYBottom = clipped[verb].fY; // was activePtr->fCloseCall ? bottom :
2074
2075 // clearing the fSkip/fCloseCall bit here means that trailing edges
2076 // fall out of sync, if one edge is long and another is a series of short pieces
2077 // if fSkip/fCloseCall is set, need to recompute coincidence/too-close-to-call
2078 // after advancing
2079 // another approach would be to restrict bottom to smaller part of close call
2080 // maybe this is already happening with coincidence when intersection is computed,
2081 // and needs to be added to the close call computation as well
2082 // this is hard to do because that the bottom is important is not known when
2083 // the lines are intersected; only when the computation for edge sorting is done
2084 // does the need for new bottoms become apparent.
2085 // maybe this is good incentive to scrap the current sort and do an insertion
2086 // sort that can take this into consideration when the x value is computed
2087
2088 // FIXME: initialized in sortHorizontal, cleared here as well so
2089 // that next edge is not skipped -- but should skipped edges ever
2090 // continue? (probably not)
caryclark@google.com752b60e2012-03-22 21:11:17 +00002091 aboveBottom = clipped[verb].fY < bottom;
2092 if (clipped[0].fY != clipped[verb].fY) {
2093 activePtr->fSkip = false;
2094 activePtr->fCloseCall = false;
2095 aboveBottom &= !activePtr->fCloseCall;
2096 } else {
2097 if (activePtr->fSkip || activePtr->fCloseCall) {
2098 SkDebugf("== %1.9g\n", clippedPts[0].fY);
2099 }
2100 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002101 } while (moreToDo & aboveBottom);
2102 } while ((moreToDo || activePtr->advance()) & aboveBottom);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002103 }
2104}
2105
caryclark@google.comc6825902012-02-03 22:07:47 +00002106void simplify(const SkPath& path, bool asFill, SkPath& simple) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002107 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
2108 int windingMask = (path.getFillType() & 1) ? 1 : -1;
2109 simple.reset();
2110 simple.setFillType(SkPath::kEvenOdd_FillType);
caryclark@google.comc6825902012-02-03 22:07:47 +00002111 // turn path into list of edges increasing in y
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002112 // if an edge is a quad or a cubic with a y extrema, note it, but leave it
2113 // unbroken. Once we have a list, sort it, then walk the list (walk edges
2114 // twice that have y extrema's on top) and detect crossings -- look for raw
2115 // bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +00002116 SkTArray<InEdge> edges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002117 SkTDArray<HorizontalEdge> horizontalEdges;
2118 InEdgeBuilder builder(path, asFill, edges, horizontalEdges);
caryclark@google.com6680fb12012-02-07 22:10:51 +00002119 SkTDArray<InEdge*> edgeList;
caryclark@google.com6680fb12012-02-07 22:10:51 +00002120 InEdge edgeSentinel;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002121 makeEdgeList(edges, edgeSentinel, edgeList);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002122 SkTDArray<HorizontalEdge*> horizontalList;
2123 HorizontalEdge horizontalSentinel;
2124 makeHorizontalList(horizontalEdges, horizontalSentinel, horizontalList);
caryclark@google.com6680fb12012-02-07 22:10:51 +00002125 InEdge** currentPtr = edgeList.begin();
caryclark@google.com4917f172012-03-05 22:01:21 +00002126 if (!currentPtr) {
2127 return;
2128 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002129 // find all intersections between edges
2130// beyond looking for horizontal intercepts, we need to know if any active edges
2131// intersect edges below 'bottom', but above the active edge segment.
2132// maybe it makes more sense to compute all intercepts before doing anything
2133// else, since the intercept list is long-lived, at least in the current design.
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002134 SkScalar y = (*currentPtr)->fBounds.fTop;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002135 HorizontalEdge** currentHorizontal = horizontalList.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00002136 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +00002137 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002138 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002139 NULL, y, asFill, lastPtr);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002140 if (lastPtr > currentPtr) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002141 if (currentHorizontal) {
2142 if ((*currentHorizontal)->fY < SK_ScalarMax) {
2143 addBottomT(currentPtr, lastPtr, currentHorizontal);
2144 }
2145 currentHorizontal = advanceHorizontal(currentHorizontal, bottom);
2146 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00002147 addIntersectingTs(currentPtr, lastPtr);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002148 }
2149 y = bottom;
2150 currentPtr = advanceEdges(NULL, currentPtr, lastPtr, y);
2151 } while (*currentPtr != &edgeSentinel);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002152
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002153#if DEBUG_DUMP
2154 InEdge** debugPtr = edgeList.begin();
2155 do {
2156 (*debugPtr++)->dump();
2157 } while (*debugPtr != &edgeSentinel);
2158#endif
caryclark@google.com752b60e2012-03-22 21:11:17 +00002159
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002160 // walk the sorted edges from top to bottom, computing accumulated winding
2161 SkTDArray<ActiveEdge> activeEdges;
2162 OutEdgeBuilder outBuilder(asFill);
2163 currentPtr = edgeList.begin();
2164 y = (*currentPtr)->fBounds.fTop;
2165 do {
2166 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
2167 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
2168 &activeEdges, y, asFill, lastPtr);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002169#if DEBUG_BOTTOM
2170 SkDebugf("%s findBottom bottom=%1.9g\n", __FUNCTION__, bottom);
2171#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002172 if (lastPtr > currentPtr) {
2173 bottom = computeInterceptBottom(activeEdges, y, bottom);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002174#if DEBUG_BOTTOM
2175 SkDebugf("%s computeInterceptBottom bottom=%1.9g\n", __FUNCTION__, bottom);
2176#endif
caryclark@google.comc17972e2012-02-20 21:33:22 +00002177 SkTDArray<ActiveEdge*> activeEdgeList;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002178 sortHorizontal(activeEdges, activeEdgeList, y);
2179 bottom = adjustCoincident(activeEdgeList, windingMask, y, bottom,
2180 outBuilder);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002181#if DEBUG_BOTTOM
2182 SkDebugf("%s adjustCoincident bottom=%1.9g\n", __FUNCTION__, bottom);
2183#endif
2184 stitchEdge(activeEdgeList, y, bottom, windingMask, asFill, outBuilder);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002185 }
caryclark@google.comc6825902012-02-03 22:07:47 +00002186 y = bottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002187 // OPTIMIZATION: as edges expire, InEdge allocations could be released
2188 currentPtr = advanceEdges(&activeEdges, currentPtr, lastPtr, y);
caryclark@google.comc6825902012-02-03 22:07:47 +00002189 } while (*currentPtr != &edgeSentinel);
caryclark@google.comc6825902012-02-03 22:07:47 +00002190 // assemble output path from string of pts, verbs
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002191 outBuilder.bridge();
2192 outBuilder.assemble(simple);
caryclark@google.comc6825902012-02-03 22:07:47 +00002193}