blob: 70fa8e679c3388f880722ce4d9ce9a4fd92b4e7c [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.com198e0542012-03-30 18:47:02 +000019#if 0 // 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
caryclark@google.com198e0542012-03-30 18:47:02 +000033#define DEBUG_SPLIT 0
caryclark@google.com752b60e2012-03-22 21:11:17 +000034
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000035#else
caryclark@google.comd88e0892012-03-27 13:23:51 +000036const bool gShowDebugf = true; // FIXME: remove once debugging is complete
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000037
38#define DEBUG_DUMP 01
39#define DEBUG_ADD 01
40#define DEBUG_ADD_INTERSECTING_TS 0
41#define DEBUG_ADD_BOTTOM_TS 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000042#define DEBUG_ABOVE_BELOW 01
caryclark@google.coma5764232012-03-28 16:20:21 +000043#define DEBUG_ACTIVE_LESS_THAN 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000044#define DEBUG_SORT_HORIZONTAL 01
45#define DEBUG_OUT 01
46#define DEBUG_OUT_LESS_THAN 0
47#define DEBUG_ADJUST_COINCIDENT 1
caryclark@google.com198e0542012-03-30 18:47:02 +000048#define DEBUG_BOTTOM 0
49#define DEBUG_SPLIT 1
caryclark@google.com752b60e2012-03-22 21:11:17 +000050
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000051#endif
52
caryclark@google.com6680fb12012-02-07 22:10:51 +000053static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
caryclark@google.coma5764232012-03-28 16:20:21 +000054 Intersections& intersections) {
55 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
56 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
57 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
58}
59
60static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
61 Intersections& intersections) {
62 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
63 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +000064 intersect(aQuad, bLine, intersections);
65 return intersections.fUsed;
caryclark@google.coma5764232012-03-28 16:20:21 +000066}
67
68static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
69 Intersections& intersections) {
70 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
71 {a[3].fX, a[3].fY}};
72 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +000073 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
caryclark@google.coma5764232012-03-28 16:20:21 +000074}
75
76static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
77 Intersections& intersections) {
78 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
79 const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +000080 intersect(aQuad, bQuad, intersections);
81 return intersections.fUsed;
caryclark@google.coma5764232012-03-28 16:20:21 +000082}
83
84static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
85 Intersections& intersections) {
86 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
87 {a[3].fX, a[3].fY}};
88 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
89 {b[3].fX, b[3].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +000090 intersect(aCubic, bCubic, intersections);
91 return intersections.fUsed;
caryclark@google.comc6825902012-02-03 22:07:47 +000092}
93
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000094static int LineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
95 SkScalar y, double aRange[2]) {
caryclark@google.coma5764232012-03-28 16:20:21 +000096 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000097 return horizontalLineIntersect(aLine, left, right, y, aRange);
caryclark@google.comc6825902012-02-03 22:07:47 +000098}
99
caryclark@google.com198e0542012-03-30 18:47:02 +0000100static int QuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
101 SkScalar y, double aRange[3]) {
102 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
103 return horizontalIntersect(aQuad, left, right, y, aRange);
104}
105
106static int CubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
107 SkScalar y, double aRange[4]) {
108 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
109 {a[3].fX, a[3].fY}};
110 return horizontalIntersect(aCubic, left, right, y, aRange);
111}
112
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000113static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000114 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000115 double x, y;
caryclark@google.coma5764232012-03-28 16:20:21 +0000116 xy_at_t(line, t, x, y);
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000117 out->fX = SkDoubleToScalar(x);
118 out->fY = SkDoubleToScalar(y);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000119}
120
caryclark@google.coma5764232012-03-28 16:20:21 +0000121static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
122 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
123 double x, y;
124 xy_at_t(quad, t, x, y);
125 out->fX = SkDoubleToScalar(x);
126 out->fY = SkDoubleToScalar(y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000127}
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000128
caryclark@google.coma5764232012-03-28 16:20:21 +0000129static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
130 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
131 {a[3].fX, a[3].fY}};
132 double x, y;
133 xy_at_t(cubic, t, x, y);
134 out->fX = SkDoubleToScalar(x);
135 out->fY = SkDoubleToScalar(y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000136}
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000137
caryclark@google.com6680fb12012-02-07 22:10:51 +0000138static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000139 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com6680fb12012-02-07 22:10:51 +0000140 double y;
141 xy_at_t(aLine, t, *(double*) 0, y);
142 return SkDoubleToScalar(y);
143}
144
caryclark@google.coma5764232012-03-28 16:20:21 +0000145static SkScalar QuadYAtT(const SkPoint a[3], double t) {
146 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
147 double y;
148 xy_at_t(quad, t, *(double*) 0, y);
149 return SkDoubleToScalar(y);
150}
151
152static SkScalar CubicYAtT(const SkPoint a[4], double t) {
153 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
154 {a[3].fX, a[3].fY}};
155 double y;
156 xy_at_t(cubic, t, *(double*) 0, y);
157 return SkDoubleToScalar(y);
158}
159
caryclark@google.com6680fb12012-02-07 22:10:51 +0000160static void LineSubDivide(const SkPoint a[2], double startT, double endT,
161 SkPoint sub[2]) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000162 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com6680fb12012-02-07 22:10:51 +0000163 _Line dst;
164 sub_divide(aLine, startT, endT, dst);
165 sub[0].fX = SkDoubleToScalar(dst[0].x);
166 sub[0].fY = SkDoubleToScalar(dst[0].y);
167 sub[1].fX = SkDoubleToScalar(dst[1].x);
168 sub[1].fY = SkDoubleToScalar(dst[1].y);
169}
170
caryclark@google.coma5764232012-03-28 16:20:21 +0000171static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
172 SkPoint sub[3]) {
173 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
174 Quadratic dst;
175 sub_divide(aQuad, startT, endT, dst);
176 sub[0].fX = SkDoubleToScalar(dst[0].x);
177 sub[0].fY = SkDoubleToScalar(dst[0].y);
178 sub[1].fX = SkDoubleToScalar(dst[1].x);
179 sub[1].fY = SkDoubleToScalar(dst[1].y);
180 sub[2].fX = SkDoubleToScalar(dst[2].x);
181 sub[2].fY = SkDoubleToScalar(dst[2].y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000182}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000183
caryclark@google.coma5764232012-03-28 16:20:21 +0000184static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
185 SkPoint sub[4]) {
186 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
187 {a[3].fX, a[3].fY}};
188 Cubic dst;
189 sub_divide(aCubic, startT, endT, dst);
190 sub[0].fX = SkDoubleToScalar(dst[0].x);
191 sub[0].fY = SkDoubleToScalar(dst[0].y);
192 sub[1].fX = SkDoubleToScalar(dst[1].x);
193 sub[1].fY = SkDoubleToScalar(dst[1].y);
194 sub[2].fX = SkDoubleToScalar(dst[2].x);
195 sub[2].fY = SkDoubleToScalar(dst[2].y);
196 sub[3].fX = SkDoubleToScalar(dst[3].x);
197 sub[3].fY = SkDoubleToScalar(dst[3].y);
198}
199
caryclark@google.comc6825902012-02-03 22:07:47 +0000200/*
201list of edges
202bounds for edge
203sort
204active T
205
206if a contour's bounds is outside of the active area, no need to create edges
207*/
208
209/* given one or more paths,
210 find the bounds of each contour, select the active contours
211 for each active contour, compute a set of edges
212 each edge corresponds to one or more lines and curves
213 leave edges unbroken as long as possible
214 when breaking edges, compute the t at the break but leave the control points alone
215
216 */
217
218void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray) {
219 SkPath::Iter iter(path, false);
220 SkPoint pts[4];
221 SkPath::Verb verb;
222 SkRect bounds;
223 bounds.setEmpty();
224 int count = 0;
225 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
226 switch (verb) {
227 case SkPath::kMove_Verb:
228 if (!bounds.isEmpty()) {
229 *boundsArray.append() = bounds;
230 }
231 bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
232 count = 0;
233 break;
234 case SkPath::kLine_Verb:
235 count = 1;
236 break;
237 case SkPath::kQuad_Verb:
238 count = 2;
239 break;
240 case SkPath::kCubic_Verb:
241 count = 3;
242 break;
243 case SkPath::kClose_Verb:
244 count = 0;
245 break;
246 default:
247 SkDEBUGFAIL("bad verb");
248 return;
249 }
250 for (int i = 1; i <= count; ++i) {
251 bounds.growToInclude(pts[i].fX, pts[i].fY);
252 }
253 }
254}
255
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000256static bool extendLine(const SkPoint line[2], const SkPoint& add) {
257 // FIXME: allow this to extend lines that have slopes that are nearly equal
258 SkScalar dx1 = line[1].fX - line[0].fX;
259 SkScalar dy1 = line[1].fY - line[0].fY;
260 SkScalar dx2 = add.fX - line[0].fX;
261 SkScalar dy2 = add.fY - line[0].fY;
262 return dx1 * dy2 == dx2 * dy1;
263}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000264
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000265// OPTIMIZATION: this should point to a list of input data rather than duplicating
266// the line data here. This would reduce the need to assemble the results.
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000267struct OutEdge {
268 bool operator<(const OutEdge& rh) const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000269 const SkPoint& first = fPts[0];
270 const SkPoint& rhFirst = rh.fPts[0];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000271 return first.fY == rhFirst.fY
272 ? first.fX < rhFirst.fX
273 : first.fY < rhFirst.fY;
274 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000275
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000276 SkPoint fPts[4];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000277 int fID; // id of edge generating data
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000278 uint8_t fVerb; // FIXME: not read from everywhere
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000279 bool fCloseCall; // edge is trimmable if not originally coincident
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000280};
281
caryclark@google.com6680fb12012-02-07 22:10:51 +0000282class OutEdgeBuilder {
283public:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000284 OutEdgeBuilder(bool fill)
285 : fFill(fill) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000286 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000287
caryclark@google.coma5764232012-03-28 16:20:21 +0000288 void addCurve(const SkPoint line[4], SkPath::Verb verb, int id,
289 bool closeCall) {
caryclark@google.comc17972e2012-02-20 21:33:22 +0000290 OutEdge& newEdge = fEdges.push_back();
caryclark@google.coma5764232012-03-28 16:20:21 +0000291 memcpy(newEdge.fPts, line, (verb + 1) * sizeof(SkPoint));
292 newEdge.fVerb = verb;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000293 newEdge.fID = id;
294 newEdge.fCloseCall = closeCall;
295 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000296
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000297 bool trimLine(SkScalar y, int id) {
298 size_t count = fEdges.count();
299 while (count-- != 0) {
300 OutEdge& edge = fEdges[count];
301 if (edge.fID != id) {
302 continue;
303 }
304 if (edge.fCloseCall) {
305 return false;
306 }
307 SkASSERT(edge.fPts[0].fY <= y);
308 if (edge.fPts[1].fY <= y) {
309 continue;
310 }
311 edge.fPts[1].fX = edge.fPts[0].fX + (y - edge.fPts[0].fY)
312 * (edge.fPts[1].fX - edge.fPts[0].fX)
313 / (edge.fPts[1].fY - edge.fPts[0].fY);
314 edge.fPts[1].fY = y;
315 if (gShowDebugf) {
316 SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id,
317 edge.fPts[1].fX, y);
318 }
319 return true;
320 }
321 return false;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000322 }
323
324 void assemble(SkPath& simple) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000325 size_t listCount = fEdges.count();
326 if (listCount == 0) {
327 return;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000328 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000329 do {
330 size_t listIndex = 0;
331 int advance = 1;
332 while (listIndex < listCount && fTops[listIndex] == 0) {
333 ++listIndex;
334 }
335 if (listIndex >= listCount) {
336 break;
337 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000338 int closeEdgeIndex = -listIndex - 1;
caryclark@google.coma5764232012-03-28 16:20:21 +0000339 SkPoint firstPt, lastCurve[4];
340 uint8_t lastVerb;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000341 bool doMove = true;
342 int edgeIndex;
343 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000344 SkPoint* ptArray = fEdges[listIndex].fPts;
345 uint8_t verb = fEdges[listIndex].fVerb;
346 SkPoint* start, * end;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000347 if (advance < 0) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000348 start = &ptArray[verb];
349 end = &ptArray[0];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000350 } else {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000351 start = &ptArray[0];
352 end = &ptArray[verb];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000353 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000354 if (doMove) {
355 firstPt = *start;
356 simple.moveTo(start->fX, start->fY);
357 if (gShowDebugf) {
358 SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
359 start->fX, start->fY);
360 }
361 lastCurve[0] = *start;
362 if (verb == SkPath::kQuad_Verb) {
363 lastCurve[1] = ptArray[1];
364 } else if (verb == SkPath::kCubic_Verb) {
365 if (advance < 0) {
366 lastCurve[1] = ptArray[2];
367 lastCurve[2] = ptArray[1];
368 } else {
369 lastCurve[1] = ptArray[1];
370 lastCurve[2] = ptArray[2];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000371 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000372 }
373 lastCurve[verb] = *end;
374 lastVerb = verb;
375 doMove = false;
376 } else {
377 bool gap = lastCurve[verb] != *start;
378 if (gap) {
379 // FIXME: see comment in bridge -- this probably
380 // conceals errors
381 SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY, start->fY) <= 10);
382 switch (lastVerb) {
383 case SkPath::kLine_Verb:
384 simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
385 break;
386 case SkPath::kQuad_Verb:
387 simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
388 lastCurve[2].fX, lastCurve[2].fY);
389 break;
390 case SkPath::kCubic_Verb:
391 simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
392 lastCurve[2].fX, lastCurve[2].fY,
393 lastCurve[3].fX, lastCurve[3].fY);
394 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000395 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000396 if (gShowDebugf) {
397 const char* verbStr[] = {"", "line", "quad", "cubic"};
398 SkDebugf("%s %sTo-1 (%g,%g)\n", __FUNCTION__,
399 verbStr[lastVerb], lastCurve[lastVerb].fX,
400 lastCurve[lastVerb].fY);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000401 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000402 }
403 if (gap || lastVerb != SkPath::kLine_Verb || !extendLine(lastCurve, *end)) {
404 // FIXME: see comment in bridge -- this probably
405 // conceals errors
406 SkASSERT(lastCurve[lastVerb] == *start ||
407 (fFill && UlpsDiff(lastCurve[lastVerb].fY, start->fY) <= 10));
408 simple.lineTo(start->fX, start->fY);
409 if (gShowDebugf) {
410 SkDebugf("%s lineTo (%g,%g)\n", __FUNCTION__,
411 start->fX, start->fY);
412 }
413 lastCurve[0] = *start;
414 }
415 if (verb == SkPath::kQuad_Verb) {
416 lastCurve[1] = ptArray[1];
417 } else if (verb == SkPath::kCubic_Verb) {
418 if (advance < 0) {
419 lastCurve[1] = ptArray[2];
420 lastCurve[2] = ptArray[1];
421 } else {
422 lastCurve[1] = ptArray[1];
423 lastCurve[2] = ptArray[2];
424 }
425 }
426 lastCurve[verb] = *end;
427 lastVerb = verb;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000428 }
429 if (advance < 0) {
430 edgeIndex = fTops[listIndex];
431 fTops[listIndex] = 0;
432 } else {
433 edgeIndex = fBottoms[listIndex];
434 fBottoms[listIndex] = 0;
435 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000436 if (edgeIndex) {
437 listIndex = abs(edgeIndex) - 1;
438 if (edgeIndex < 0) {
439 fTops[listIndex] = 0;
440 } else {
441 fBottoms[listIndex] = 0;
442 }
443 }
444 if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000445 if (lastCurve[lastVerb] != firstPt) {
446 switch (lastVerb) {
447 case SkPath::kLine_Verb:
448 simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
449 break;
450 case SkPath::kQuad_Verb:
451 simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
452 lastCurve[2].fX, lastCurve[2].fY);
453 break;
454 case SkPath::kCubic_Verb:
455 simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
456 lastCurve[2].fX, lastCurve[2].fY,
457 lastCurve[3].fX, lastCurve[3].fY);
458 break;
459 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000460 if (gShowDebugf) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000461 const char* verbStr[] = {"", "line", "quad", "cubic"};
462 SkDebugf("%s %sTo last (%g, %g)\n", __FUNCTION__,
463 verbStr[lastVerb],
464 lastCurve[lastVerb].fX, lastCurve[lastVerb].fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000465 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000466 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000467 simple.lineTo(firstPt.fX, firstPt.fY);
468 simple.close();
469 if (gShowDebugf) {
caryclark@google.com4917f172012-03-05 22:01:21 +0000470 SkDebugf("%s close (%g, %g)\n", __FUNCTION__,
471 firstPt.fX, firstPt.fY);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000472 }
473 break;
474 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000475 // if this and next edge go different directions
476 if (advance > 0 ^ edgeIndex < 0) {
477 advance = -advance;
478 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000479 } while (edgeIndex);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000480 } while (true);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000481 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000482
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000483 // sort points by y, then x
484 // if x/y is identical, sort bottoms before tops
485 // if identical and both tops/bottoms, sort by angle
486 static bool lessThan(SkTArray<OutEdge>& edges, const int one,
487 const int two) {
488 const OutEdge& oneEdge = edges[abs(one) - 1];
489 int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
490 const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
491 const OutEdge& twoEdge = edges[abs(two) - 1];
492 int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
493 const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
494 if (startPt1.fY != startPt2.fY) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000495 #if DEBUG_OUT_LESS_THAN
496 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
497 one, two, startPt1.fY, startPt2.fY,
498 startPt1.fY < startPt2.fY ? "true" : "false");
499 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000500 return startPt1.fY < startPt2.fY;
501 }
502 if (startPt1.fX != startPt2.fX) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000503 #if DEBUG_OUT_LESS_THAN
504 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
505 one, two, startPt1.fX, startPt2.fX,
506 startPt1.fX < startPt2.fX ? "true" : "false");
507 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000508 return startPt1.fX < startPt2.fX;
509 }
510 const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
511 const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
512 SkScalar dy1 = startPt1.fY - endPt1.fY;
513 SkScalar dy2 = startPt2.fY - endPt2.fY;
514 SkScalar dy1y2 = dy1 * dy2;
515 if (dy1y2 < 0) { // different signs
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000516 #if DEBUG_OUT_LESS_THAN
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000517 SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
518 dy1 > 0 ? "true" : "false");
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000519 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000520 return dy1 > 0; // one < two if one goes up and two goes down
521 }
522 if (dy1y2 == 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000523 #if DEBUG_OUT_LESS_THAN
524 SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
525 one, two, endPt1.fX < endPt2.fX ? "true" : "false");
526 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000527 return endPt1.fX < endPt2.fX;
528 }
529 SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
530 SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000531 #if DEBUG_OUT_LESS_THAN
532 SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
533 one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
534 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000535 return dy2 > 0 ^ dx1y2 < dx2y1;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000536 }
537
caryclark@google.com6008c6562012-02-15 22:01:16 +0000538 // Sort the indices of paired points and then create more indices so
539 // assemble() can find the next edge and connect the top or bottom
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000540 void bridge() {
541 size_t index;
542 size_t count = fEdges.count();
543 if (!count) {
544 return;
545 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000546 SkASSERT(!fFill || count > 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000547 fTops.setCount(count);
548 sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
549 fBottoms.setCount(count);
550 sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000551 SkTDArray<int> order;
552 for (index = 1; index <= count; ++index) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000553 *order.append() = -index;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000554 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000555 for (index = 1; index <= count; ++index) {
556 *order.append() = index;
557 }
558 QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000559 int* lastPtr = order.end() - 1;
560 int* leftPtr = order.begin();
561 while (leftPtr < lastPtr) {
562 int leftIndex = *leftPtr;
563 int leftOutIndex = abs(leftIndex) - 1;
564 const OutEdge& left = fEdges[leftOutIndex];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000565 int* rightPtr = leftPtr + 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000566 int rightIndex = *rightPtr;
567 int rightOutIndex = abs(rightIndex) - 1;
568 const OutEdge& right = fEdges[rightOutIndex];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000569 bool pairUp = fFill;
570 if (!pairUp) {
571 const SkPoint& leftMatch =
572 left.fPts[leftIndex < 0 ? 0 : left.fVerb];
573 const SkPoint& rightMatch =
574 right.fPts[rightIndex < 0 ? 0 : right.fVerb];
575 pairUp = leftMatch == rightMatch;
576 } else {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000577 #if DEBUG_OUT
caryclark@google.comd88e0892012-03-27 13:23:51 +0000578 // FIXME : not happy that error in low bit is allowed
579 // this probably conceals error elsewhere
580 if (UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
581 right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) > 1) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000582 *fMismatches.append() = leftIndex;
583 if (rightPtr == lastPtr) {
584 *fMismatches.append() = rightIndex;
585 }
586 pairUp = false;
587 }
588 #else
caryclark@google.comd88e0892012-03-27 13:23:51 +0000589 SkASSERT(UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
590 right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) <= 10);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000591 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000592 }
593 if (pairUp) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000594 if (leftIndex < 0) {
595 fTops[leftOutIndex] = rightIndex;
596 } else {
597 fBottoms[leftOutIndex] = rightIndex;
598 }
599 if (rightIndex < 0) {
600 fTops[rightOutIndex] = leftIndex;
601 } else {
602 fBottoms[rightOutIndex] = leftIndex;
603 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000604 ++rightPtr;
605 }
606 leftPtr = rightPtr;
607 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000608#if DEBUG_OUT
609 int* mismatch = fMismatches.begin();
610 while (mismatch != fMismatches.end()) {
611 int leftIndex = *mismatch++;
612 int leftOutIndex = abs(leftIndex) - 1;
613 const OutEdge& left = fEdges[leftOutIndex];
614 const SkPoint& leftPt = left.fPts[leftIndex < 0 ? 0 : left.fVerb];
615 SkDebugf("%s left=%d %s (%1.9g,%1.9g)\n",
616 __FUNCTION__, left.fID, leftIndex < 0 ? "top" : "bot",
617 leftPt.fX, leftPt.fY);
618 }
619 SkASSERT(fMismatches.count() == 0);
620#endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000621 }
622
caryclark@google.com6008c6562012-02-15 22:01:16 +0000623protected:
caryclark@google.com6680fb12012-02-07 22:10:51 +0000624 SkTArray<OutEdge> fEdges;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000625 SkTDArray<int> fTops;
626 SkTDArray<int> fBottoms;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000627 bool fFill;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000628#if DEBUG_OUT
629 SkTDArray<int> fMismatches;
630#endif
caryclark@google.com6680fb12012-02-07 22:10:51 +0000631};
632
caryclark@google.comc6825902012-02-03 22:07:47 +0000633// Bounds, unlike Rect, does not consider a vertical line to be empty.
634struct Bounds : public SkRect {
635 static bool Intersects(const Bounds& a, const Bounds& b) {
636 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
637 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
638 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000639
caryclark@google.com6008c6562012-02-15 22:01:16 +0000640 bool isEmpty() {
641 return fLeft > fRight || fTop > fBottom
642 || fLeft == fRight && fTop == fBottom
643 || isnan(fLeft) || isnan(fRight)
644 || isnan(fTop) || isnan(fBottom);
645 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000646};
647
caryclark@google.com4917f172012-03-05 22:01:21 +0000648class Intercepts {
649public:
650 Intercepts()
651 : fTopIntercepts(0)
caryclark@google.com198e0542012-03-30 18:47:02 +0000652 , fBottomIntercepts(0)
653 , fExplicit(false) {
654 }
655
656 Intercepts& operator=(const Intercepts& src) {
657 fTs = src.fTs;
658 fTopIntercepts = src.fTopIntercepts;
659 fBottomIntercepts = src.fBottomIntercepts;
660 }
661
662 // OPTIMIZATION: remove this function if it's never called
663 double t(int tIndex) const {
664 if (tIndex == 0) {
665 return 0;
666 }
667 if (tIndex > fTs.count()) {
668 return 1;
669 }
670 return fTs[tIndex - 1];
caryclark@google.com4917f172012-03-05 22:01:21 +0000671 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000672
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000673#if DEBUG_DUMP
caryclark@google.coma5764232012-03-28 16:20:21 +0000674 void dump(const SkPoint* pts, SkPath::Verb verb) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000675 const char className[] = "Intercepts";
676 const int tab = 8;
677 for (int i = 0; i < fTs.count(); ++i) {
678 SkPoint out;
caryclark@google.coma5764232012-03-28 16:20:21 +0000679 switch (verb) {
680 case SkPath::kLine_Verb:
681 LineXYAtT(pts, fTs[i], &out);
682 break;
683 case SkPath::kQuad_Verb:
684 QuadXYAtT(pts, fTs[i], &out);
685 break;
686 case SkPath::kCubic_Verb:
687 CubicXYAtT(pts, fTs[i], &out);
688 break;
689 default:
690 SkASSERT(0);
691 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000692 SkDebugf("%*s.fTs[%d]=%1.9g (%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000693 className, i, fTs[i], out.fX, out.fY);
694 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000695 SkDebugf("%*s.fTopIntercepts=%u\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000696 className, fTopIntercepts);
caryclark@google.com198e0542012-03-30 18:47:02 +0000697 SkDebugf("%*s.fBottomIntercepts=%u\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000698 className, fBottomIntercepts);
699 }
700#endif
701
caryclark@google.comc6825902012-02-03 22:07:47 +0000702 SkTDArray<double> fTs;
caryclark@google.com198e0542012-03-30 18:47:02 +0000703 unsigned char fTopIntercepts; // 0=init state 1=1 edge >1=multiple edges
704 unsigned char fBottomIntercepts;
705 bool fExplicit; // if set, suppress 0 and 1
706
caryclark@google.comc6825902012-02-03 22:07:47 +0000707};
708
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000709struct HorizontalEdge {
710 bool operator<(const HorizontalEdge& rh) const {
711 return fY == rh.fY ? fLeft == rh.fLeft ? fRight < rh.fRight
712 : fLeft < rh.fLeft : fY < rh.fY;
713 }
714
715#if DEBUG_DUMP
716 void dump() {
717 const char className[] = "HorizontalEdge";
718 const int tab = 4;
caryclark@google.com752b60e2012-03-22 21:11:17 +0000719 SkDebugf("%*s.fLeft=%1.9g\n", tab + sizeof(className), className, fLeft);
720 SkDebugf("%*s.fRight=%1.9g\n", tab + sizeof(className), className, fRight);
721 SkDebugf("%*s.fY=%1.9g\n", tab + sizeof(className), className, fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000722 }
723#endif
724
725 SkScalar fLeft;
726 SkScalar fRight;
727 SkScalar fY;
728};
729
caryclark@google.com6680fb12012-02-07 22:10:51 +0000730struct InEdge {
731 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000732 return fBounds.fTop == rh.fBounds.fTop
733 ? fBounds.fLeft < rh.fBounds.fLeft
734 : fBounds.fTop < rh.fBounds.fTop;
735 }
736
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000737 // Avoid collapsing t values that are close to the same since
738 // we walk ts to describe consecutive intersections. Since a pair of ts can
739 // be nearly equal, any problems caused by this should be taken care
740 // of later.
741 int add(double* ts, size_t count, ptrdiff_t verbIndex) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000742 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
caryclark@google.com6008c6562012-02-15 22:01:16 +0000743 bool foundIntercept = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000744 int insertedAt = -1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000745 Intercepts& intercepts = fIntercepts[verbIndex];
caryclark@google.comc6825902012-02-03 22:07:47 +0000746 for (size_t index = 0; index < count; ++index) {
747 double t = ts[index];
caryclark@google.com4917f172012-03-05 22:01:21 +0000748 if (t <= 0) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000749 intercepts.fTopIntercepts <<= 1;
caryclark@google.com4917f172012-03-05 22:01:21 +0000750 fContainsIntercepts |= ++intercepts.fTopIntercepts > 1;
751 continue;
752 }
753 if (t >= 1) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000754 intercepts.fBottomIntercepts <<= 1;
caryclark@google.com4917f172012-03-05 22:01:21 +0000755 fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000756 continue;
757 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000758 fIntersected = true;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000759 foundIntercept = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000760 size_t tCount = intercepts.fTs.count();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000761 double delta;
caryclark@google.comc6825902012-02-03 22:07:47 +0000762 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
763 if (t <= intercepts.fTs[idx2]) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000764 // FIXME: ? if (t < intercepts.fTs[idx2]) // failed
765 delta = intercepts.fTs[idx2] - t;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000766 if (delta > 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000767 insertedAt = idx2;
caryclark@google.comc6825902012-02-03 22:07:47 +0000768 *intercepts.fTs.insert(idx2) = t;
caryclark@google.comc6825902012-02-03 22:07:47 +0000769 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000770 goto nextPt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000771 }
772 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000773 if (tCount == 0 || (delta = t - intercepts.fTs[tCount - 1]) > 0) {
774 insertedAt = tCount;
caryclark@google.comc6825902012-02-03 22:07:47 +0000775 *intercepts.fTs.append() = t;
776 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000777 nextPt:
778 ;
caryclark@google.comc6825902012-02-03 22:07:47 +0000779 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000780 fContainsIntercepts |= foundIntercept;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000781 return insertedAt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000782 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000783
784 void addPartial(SkTArray<InEdge>& edges, int id, int ptStart, int ptEnd,
785 int verbStart, int verbEnd) {
786 InEdge* edge = edges.push_back_n(1);
787 int verbCount = verbEnd - verbStart;
788 edge->fIntercepts.push_back_n(verbCount);
789 uint8_t* verbs = &fVerbs[verbStart];
790 for (int ceptIdx = 0; ceptIdx < verbCount; ++ceptIdx) {
791 edge->fIntercepts[ceptIdx] = fIntercepts[verbStart + ceptIdx];
792 }
793 edge->fPts.append(ptEnd - ptStart, &fPts[ptStart]);
794 edge->fVerbs.append(verbCount, &fVerbs[verbStart]);
795 edge->setBounds();
796 edge->fID = id + edges.count();
797 edge->fWinding = fWinding;
798 edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
799 }
800
801 void addSplit(SkTArray<InEdge>& edges, int id, SkPoint* pts, uint8_t verb,
802 double* ts, int tCount, bool flipped) {
803 InEdge* edge = edges.push_back_n(1);
804 edge->fIntercepts.push_back_n(1);
805 edge->fIntercepts[0].fTs.append(tCount, ts);
806 edge->fIntercepts[0].fExplicit = true;
807 edge->fPts.append(verb, pts);
808 edge->fVerbs.append(1, &verb);
809 edge->setBounds();
810 edge->fID = id + edges.count();
811 edge->fWinding = fWinding;
812 edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
813 if (flipped) {
814 flip();
815 }
816 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000817
caryclark@google.com6680fb12012-02-07 22:10:51 +0000818 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000819 // FIXME: in the pathological case where there is a ton of edges, binary search?
820 size_t count = fCached.count();
821 for (size_t index = 0; index < count; ++index) {
822 if (edge == fCached[index]) {
823 return true;
824 }
825 if (edge < fCached[index]) {
826 *fCached.insert(index) = edge;
827 return false;
828 }
829 }
830 *fCached.append() = edge;
831 return false;
832 }
833
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000834 void complete(signed char winding, int id) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000835 setBounds();
836 fIntercepts.push_back_n(fVerbs.count());
837 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
838 flip();
839 }
840 fContainsIntercepts = fIntersected = false;
841 fID = id;
842 }
843
844 void flip() {
845 size_t index;
846 size_t last = fPts.count() - 1;
847 for (index = 0; index < last; ++index, --last) {
848 SkTSwap<SkPoint>(fPts[index], fPts[last]);
849 }
850 last = fVerbs.count() - 1;
851 for (index = 0; index < last; ++index, --last) {
852 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
853 }
854 }
855
856 void reset() {
857 fCached.reset();
858 fIntercepts.reset();
859 fPts.reset();
860 fVerbs.reset();
861 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
862 fID = -1;
863 fWinding = 0;
864 fContainsIntercepts = false;
865 fIntersected = false;
866 }
867
868 void setBounds() {
caryclark@google.comc6825902012-02-03 22:07:47 +0000869 SkPoint* ptPtr = fPts.begin();
870 SkPoint* ptLast = fPts.end();
871 if (ptPtr == ptLast) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000872 SkDebugf("%s empty edge\n", __FUNCTION__);
caryclark@google.comc6825902012-02-03 22:07:47 +0000873 SkASSERT(0);
874 // FIXME: delete empty edge?
875 return;
876 }
877 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
878 ++ptPtr;
879 while (ptPtr != ptLast) {
880 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
881 ++ptPtr;
882 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000883 }
884
885 void splitInflectionPts(SkTArray<InEdge>& edges, int idStart) {
886 if (!fIntersected) {
887 return;
888 }
889 uint8_t* verbs = fVerbs.begin();
890 SkPoint* pts = fPts.begin();
891 int lastVerb = 0;
892 int lastPt = 0;
893 uint8_t verb;
894 bool edgeSplit = false;
895 for (int ceptIdx = 0; ceptIdx < fIntercepts.count(); ++ceptIdx, pts += verb) {
896 Intercepts& intercepts = fIntercepts[ceptIdx];
897 verb = *verbs++;
898 if (verb <= SkPath::kLine_Verb) {
899 continue;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000900 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000901 size_t tCount = intercepts.fTs.count();
902 if (!tCount) {
903 continue;
904 }
905 size_t tIndex = -1;
906 SkScalar y = pts[0].fY;
907 int lastSplit = 0;
908 int firstSplit = -1;
909 bool curveSplit = false;
910 while (++tIndex < tCount) {
911 double nextT = intercepts.fTs[tIndex];
912 SkScalar nextY = verb == SkPath::kQuad_Verb
913 ? QuadYAtT(pts, nextT) : CubicYAtT(pts, nextT);
914 if (nextY < y) {
915 edgeSplit = curveSplit = true;
916 if (firstSplit < 0) {
917 firstSplit = tIndex;
918 int nextPt = pts - fPts.begin();
919 int nextVerb = verbs - 1 - fVerbs.begin();
920 if (lastVerb < nextVerb) {
921 addPartial(edges, idStart, lastPt, nextPt,
922 lastVerb, nextVerb);
923 #if DEBUG_SPLIT
924 SkDebugf("%s addPartial 1 edge=%d\n", __FUNCTION__,
925 fID);
926 #endif
927 }
928 lastPt = nextPt;
929 lastVerb = nextVerb;
930 }
931 } else {
932 if (firstSplit >= 0) {
933 if (lastSplit < firstSplit) {
934 addSplit(edges, idStart, pts, verb,
935 &intercepts.fTs[lastSplit],
936 firstSplit - lastSplit, false);
937 #if DEBUG_SPLIT
938 SkDebugf("%s addSplit 1 edge=%d tIndex=%d,%d\n",
939 __FUNCTION__, fID, lastSplit, firstSplit);
940 #endif
941 }
942 addSplit(edges, idStart, pts, verb,
943 &intercepts.fTs[firstSplit],
944 tIndex - firstSplit, true);
945 #if DEBUG_SPLIT
946 SkDebugf("%s addSplit 2 edge=%d tIndex=%d,%d flip\n",
947 __FUNCTION__, fID, firstSplit, tIndex);
948 #endif
949 lastSplit = tIndex;
950 firstSplit = -1;
951 }
952 }
953 y = nextY;
954 }
955 if (curveSplit) {
956 if (firstSplit < 0) {
957 firstSplit = lastSplit;
958 } else {
959 addSplit(edges, idStart, pts, verb,
960 &intercepts.fTs[lastSplit], firstSplit - lastSplit,
961 false);
962 #if DEBUG_SPLIT
963 SkDebugf("%s addSplit 3 edge=%d tIndex=%d,%d\n", __FUNCTION__,
964 fID, lastSplit, firstSplit);
965 #endif
966 }
967 addSplit(edges, idStart, pts, verb,
968 &intercepts.fTs[firstSplit], tIndex - firstSplit,
969 pts[verb].fY < y);
970 #if DEBUG_SPLIT
971 SkDebugf("%s addSplit 4 edge=%d tIndex=%d,%d %s\n", __FUNCTION__,
972 fID, firstSplit, tIndex, pts[verb].fY < y ? "flip" : "");
973 #endif
caryclark@google.com6680fb12012-02-07 22:10:51 +0000974 }
975 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000976 // collapse remainder -- if there's nothing left, clear it somehow?
977 if (edgeSplit) {
978 int nextVerb = verbs - 1 - fVerbs.begin();
979 if (lastVerb < nextVerb) {
980 int nextPt = pts - fPts.begin();
981 addPartial(edges, idStart, lastPt, nextPt,
982 lastVerb, nextVerb);
983 #if DEBUG_SPLIT
984 SkDebugf("%s addPartial 2 edge=%d\n", __FUNCTION__, fID);
985 #endif
986 }
987 // OPTIMIZATION: reuse the edge instead of marking it empty
988 reset();
989 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000990 }
991
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000992#if DEBUG_DUMP
993 void dump() {
994 int i;
995 const char className[] = "InEdge";
996 const int tab = 4;
997 SkDebugf("InEdge %p (edge=%d)\n", this, fID);
998 for (i = 0; i < fCached.count(); ++i) {
999 SkDebugf("%*s.fCached[%d]=0x%08x\n", tab + sizeof(className),
1000 className, i, fCached[i]);
1001 }
1002 uint8_t* verbs = fVerbs.begin();
1003 SkPoint* pts = fPts.begin();
1004 for (i = 0; i < fIntercepts.count(); ++i) {
1005 SkDebugf("%*s.fIntercepts[%d]:\n", tab + sizeof(className),
1006 className, i);
caryclark@google.coma5764232012-03-28 16:20:21 +00001007 fIntercepts[i].dump(pts, (SkPath::Verb) *verbs);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001008 pts += *verbs++;
1009 }
1010 for (i = 0; i < fPts.count(); ++i) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001011 SkDebugf("%*s.fPts[%d]=(%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001012 className, i, fPts[i].fX, fPts[i].fY);
1013 }
1014 for (i = 0; i < fVerbs.count(); ++i) {
1015 SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className),
1016 className, i, fVerbs[i]);
1017 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001018 SkDebugf("%*s.fBounds=(%1.9g. %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001019 className, fBounds.fLeft, fBounds.fTop,
1020 fBounds.fRight, fBounds.fBottom);
1021 SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className,
1022 fWinding);
1023 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
1024 className, fContainsIntercepts);
caryclark@google.com198e0542012-03-30 18:47:02 +00001025 SkDebugf("%*s.fIntersected=%d\n", tab + sizeof(className),
1026 className, fIntersected);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001027 }
1028#endif
1029
caryclark@google.com198e0542012-03-30 18:47:02 +00001030 // FIXME: temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +00001031 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +00001032 SkTArray<Intercepts> fIntercepts; // one per verb
caryclark@google.com4917f172012-03-05 22:01:21 +00001033
caryclark@google.comc6825902012-02-03 22:07:47 +00001034 // persistent data
1035 SkTDArray<SkPoint> fPts;
1036 SkTDArray<uint8_t> fVerbs;
1037 Bounds fBounds;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001038 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +00001039 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001040 bool fContainsIntercepts;
caryclark@google.com198e0542012-03-30 18:47:02 +00001041 bool fIntersected;
caryclark@google.comc6825902012-02-03 22:07:47 +00001042};
1043
caryclark@google.com6680fb12012-02-07 22:10:51 +00001044class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +00001045public:
1046
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001047InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges,
1048 SkTDArray<HorizontalEdge>& horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +00001049 : fPath(path)
1050 , fCurrentEdge(NULL)
1051 , fEdges(edges)
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001052 , fID(0)
1053 , fHorizontalEdges(horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +00001054 , fIgnoreHorizontal(ignoreHorizontal)
caryclark@google.com198e0542012-03-30 18:47:02 +00001055 , fContainsCurves(false)
caryclark@google.comc6825902012-02-03 22:07:47 +00001056{
1057 walk();
1058}
1059
caryclark@google.com198e0542012-03-30 18:47:02 +00001060bool containsCurves() const {
1061 return fContainsCurves;
1062}
1063
1064int nextID() {
1065 return ++fID;
1066}
1067
caryclark@google.comc6825902012-02-03 22:07:47 +00001068protected:
1069
1070void addEdge() {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001071 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +00001072 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
1073 fPtOffset = 1;
1074 *fCurrentEdge->fVerbs.append() = fVerb;
1075}
1076
caryclark@google.com6008c6562012-02-15 22:01:16 +00001077bool complete() {
1078 if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
caryclark@google.com198e0542012-03-30 18:47:02 +00001079 fCurrentEdge->complete(fWinding, nextID());
caryclark@google.com6008c6562012-02-15 22:01:16 +00001080 fCurrentEdge = NULL;
1081 return true;
1082 }
1083 return false;
1084}
1085
caryclark@google.comc6825902012-02-03 22:07:47 +00001086int direction(int count) {
1087 fPtCount = count;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001088 if (fIgnoreHorizontal && isHorizontal()) {
caryclark@google.comc6825902012-02-03 22:07:47 +00001089 return 0;
1090 }
1091 int last = count - 1;
1092 return fPts[0].fY == fPts[last].fY
caryclark@google.com6680fb12012-02-07 22:10:51 +00001093 ? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX
1094 ? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +00001095}
1096
1097bool isHorizontal() {
1098 SkScalar y = fPts[0].fY;
1099 for (int i = 1; i < fPtCount; ++i) {
1100 if (fPts[i].fY != y) {
1101 return false;
1102 }
1103 }
1104 return true;
1105}
1106
1107void startEdge() {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001108 if (!fCurrentEdge) {
1109 fCurrentEdge = fEdges.push_back_n(1);
1110 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001111 fWinding = 0;
1112 fPtOffset = 0;
1113}
1114
1115void walk() {
1116 SkPath::Iter iter(fPath, true);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001117 int winding = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +00001118 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
1119 switch (fVerb) {
1120 case SkPath::kMove_Verb:
caryclark@google.comc6825902012-02-03 22:07:47 +00001121 startEdge();
1122 continue;
1123 case SkPath::kLine_Verb:
1124 winding = direction(2);
1125 break;
1126 case SkPath::kQuad_Verb:
1127 winding = direction(3);
caryclark@google.com198e0542012-03-30 18:47:02 +00001128 fContainsCurves = true;
caryclark@google.comc6825902012-02-03 22:07:47 +00001129 break;
1130 case SkPath::kCubic_Verb:
1131 winding = direction(4);
caryclark@google.com198e0542012-03-30 18:47:02 +00001132 fContainsCurves = true;
caryclark@google.comc6825902012-02-03 22:07:47 +00001133 break;
1134 case SkPath::kClose_Verb:
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001135 SkASSERT(fCurrentEdge);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001136 complete();
caryclark@google.comc6825902012-02-03 22:07:47 +00001137 continue;
1138 default:
1139 SkDEBUGFAIL("bad verb");
1140 return;
1141 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001142 if (winding == 0) {
1143 HorizontalEdge* horizontalEdge = fHorizontalEdges.append();
1144 // FIXME: for degenerate quads and cubics, compute x extremes
1145 horizontalEdge->fLeft = fPts[0].fX;
1146 horizontalEdge->fRight = fPts[fVerb].fX;
1147 horizontalEdge->fY = fPts[0].fY;
1148 if (horizontalEdge->fLeft > horizontalEdge->fRight) {
1149 SkTSwap<SkScalar>(horizontalEdge->fLeft, horizontalEdge->fRight);
1150 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001151 if (complete()) {
1152 startEdge();
1153 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001154 continue;
1155 }
1156 if (fWinding + winding == 0) {
1157 // FIXME: if prior verb or this verb is a horizontal line, reverse
1158 // it instead of starting a new edge
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001159 SkASSERT(fCurrentEdge);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001160 if (complete()) {
1161 startEdge();
1162 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001163 }
1164 fWinding = winding;
1165 addEdge();
1166 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001167 if (!complete()) {
1168 if (fCurrentEdge) {
1169 fEdges.pop_back();
1170 }
1171 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001172}
1173
1174private:
1175 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001176 InEdge* fCurrentEdge;
1177 SkTArray<InEdge>& fEdges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001178 SkTDArray<HorizontalEdge>& fHorizontalEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +00001179 SkPoint fPts[4];
1180 SkPath::Verb fVerb;
1181 int fPtCount;
1182 int fPtOffset;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001183 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +00001184 int8_t fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +00001185 bool fIgnoreHorizontal;
caryclark@google.com198e0542012-03-30 18:47:02 +00001186 bool fContainsCurves;
caryclark@google.comc6825902012-02-03 22:07:47 +00001187};
1188
caryclark@google.com6680fb12012-02-07 22:10:51 +00001189struct WorkEdge {
1190 SkScalar bottom() const {
1191 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +00001192 }
1193
caryclark@google.com6680fb12012-02-07 22:10:51 +00001194 void init(const InEdge* edge) {
1195 fEdge = edge;
1196 fPts = edge->fPts.begin();
1197 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00001198 }
1199
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001200 bool advance() {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001201 SkASSERT(fVerb < fEdge->fVerbs.end());
1202 fPts += *fVerb++;
1203 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +00001204 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001205
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001206 SkPath::Verb lastVerb() const {
1207 SkASSERT(fVerb > fEdge->fVerbs.begin());
1208 return (SkPath::Verb) fVerb[-1];
1209 }
1210
caryclark@google.comc6825902012-02-03 22:07:47 +00001211
1212 SkPath::Verb verb() const {
1213 return (SkPath::Verb) *fVerb;
1214 }
1215
caryclark@google.com6008c6562012-02-15 22:01:16 +00001216 ptrdiff_t verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001217 return fVerb - fEdge->fVerbs.begin();
1218 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001219
caryclark@google.com6680fb12012-02-07 22:10:51 +00001220 int winding() const {
1221 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +00001222 }
1223
caryclark@google.com6680fb12012-02-07 22:10:51 +00001224 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +00001225 const SkPoint* fPts;
1226 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +00001227};
1228
caryclark@google.com6680fb12012-02-07 22:10:51 +00001229// always constructed with SkTDArray because new edges are inserted
1230// this may be a inappropriate optimization, suggesting that a separate array of
1231// ActiveEdge* may be faster to insert and search
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001232
1233// OPTIMIZATION: Brian suggests that global sorting should be unnecessary, since
1234// as active edges are introduced, only local sorting should be required
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001235class ActiveEdge {
1236public:
caryclark@google.com6008c6562012-02-15 22:01:16 +00001237 bool operator<(const ActiveEdge& rh) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001238 double topD = fAbove.fX - rh.fAbove.fX;
1239 if (rh.fAbove.fY < fAbove.fY) {
1240 topD = (rh.fBelow.fY - rh.fAbove.fY) * topD
1241 - (fAbove.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
1242 } else if (rh.fAbove.fY > fAbove.fY) {
1243 topD = (fBelow.fY - fAbove.fY) * topD
1244 + (rh.fAbove.fY - fAbove.fY) * (fBelow.fX - fAbove.fX);
1245 }
1246 double botD = fBelow.fX - rh.fBelow.fX;
1247 if (rh.fBelow.fY > fBelow.fY) {
1248 botD = (rh.fBelow.fY - rh.fAbove.fY) * botD
1249 - (fBelow.fY - rh.fBelow.fY) * (rh.fBelow.fX - rh.fAbove.fX);
1250 } else if (rh.fBelow.fY < fBelow.fY) {
1251 botD = (fBelow.fY - fAbove.fY) * botD
1252 + (rh.fBelow.fY - fBelow.fY) * (fBelow.fX - fAbove.fX);
1253 }
1254 // return sign of greater absolute value
1255 return (fabs(topD) > fabs(botD) ? topD : botD) < 0;
1256 }
1257
1258 // OPTIMIZATION: fold return statements into one
1259 bool operator_less_than(const ActiveEdge& rh) const {
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001260 if (rh.fAbove.fY - fAbove.fY > fBelow.fY - rh.fAbove.fY
1261 && fBelow.fY < rh.fBelow.fY
1262 || fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - fAbove.fY
1263 && rh.fBelow.fY < fBelow.fY) {
1264 // FIXME: need to compute distance, not check for points equal
1265 const SkPoint& check = rh.fBelow.fY <= fBelow.fY
1266 && fBelow != rh.fBelow ? rh.fBelow :
1267 rh.fAbove;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001268 #if DEBUG_ACTIVE_LESS_THAN
1269 SkDebugf("%s 1 %c %cthis (edge=%d) {%g,%g %g,%g}"
1270 " < rh (edge=%d) {%g,%g %g,%g} upls=(%d)\n", __FUNCTION__,
1271 rh.fBelow.fY <= fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
1272 (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
1273 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX) ? ' '
1274 : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
1275 rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
1276 UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
1277 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX)));
1278 #endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001279 return (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
1280 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX);
1281 }
1282 // FIXME: need to compute distance, not check for points equal
1283 const SkPoint& check = fBelow.fY <= rh.fBelow.fY
1284 && fBelow != rh.fBelow ? fBelow : fAbove;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001285 #if DEBUG_ACTIVE_LESS_THAN
1286 SkDebugf("%s 2 %c %cthis (edge=%d) {%g,%g %g,%g}"
1287 " < rh (edge=%d) {%g,%g %g,%g} upls=(%d) (%d,%d)\n", __FUNCTION__,
1288 fBelow.fY <= rh.fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
1289 (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
1290 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)
1291 ? ' ' : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
1292 rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
1293 UlpsDiff((rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX),
1294 (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)),
1295 UlpsDiff(fBelow.fX, rh.fBelow.fX), UlpsDiff(fBelow.fY, rh.fBelow.fY));
1296 #endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001297 return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
1298 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001299 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001300
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001301 // If a pair of edges are nearly coincident for some span, add a T in the
1302 // edge so it can be shortened to match the other edge. Note that another
1303 // approach is to trim the edge after it is added to the OutBuilder list --
1304 // FIXME: since this has no effect if the edge is already done (i.e.,
1305 // fYBottom >= y) maybe this can only be done by calling trimLine later.
1306 void addTatYBelow(SkScalar y) {
1307 if (fBelow.fY <= y || fYBottom >= y) {
1308 return;
1309 }
1310 addTatYInner(y);
1311 fFixBelow = true;
1312 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001313
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001314 void addTatYAbove(SkScalar y) {
1315 if (fBelow.fY <= y) {
1316 return;
1317 }
1318 addTatYInner(y);
1319 }
1320
1321 void addTatYInner(SkScalar y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001322 if (fWorkEdge.fPts[0].fY > y) {
1323 backup(y);
1324 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001325 SkScalar left = fWorkEdge.fPts[0].fX;
1326 SkScalar right = fWorkEdge.fPts[1].fX;
1327 if (left > right) {
1328 SkTSwap(left, right);
1329 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001330 double ts[2];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001331 int pts = LineIntersect(fWorkEdge.fPts, left, right, y, ts);
1332 SkASSERT(pts == 1);
1333 // An ActiveEdge or WorkEdge has no need to modify the T values computed
1334 // in the InEdge, except in the following case. If a pair of edges are
1335 // nearly coincident, this may not be detected when the edges are
1336 // intersected. Later, when sorted, and this near-coincidence is found,
1337 // an additional t value must be added, requiring the cast below.
1338 InEdge* writable = const_cast<InEdge*>(fWorkEdge.fEdge);
1339 int insertedAt = writable->add(ts, pts, fWorkEdge.verbIndex());
caryclark@google.com752b60e2012-03-22 21:11:17 +00001340 #if DEBUG_ADJUST_COINCIDENT
1341 SkDebugf("%s edge=%d y=%1.9g t=%1.9g\n", __FUNCTION__, ID(), y, ts[0]);
1342 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001343 if (insertedAt >= 0) {
1344 if (insertedAt + 1 < fTIndex) {
1345 SkASSERT(insertedAt + 2 == fTIndex);
1346 --fTIndex;
1347 }
1348 }
1349 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001350
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001351 bool advanceT() {
caryclark@google.com198e0542012-03-30 18:47:02 +00001352 SkASSERT(fTIndex <= fTs->count() - fExplicitTs);
1353 return ++fTIndex <= fTs->count() - fExplicitTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001354 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001355
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001356 bool advance() {
1357 // FIXME: flip sense of next
1358 bool result = fWorkEdge.advance();
1359 fDone = !result;
1360 initT();
1361 return result;
1362 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001363
1364 void backup(SkScalar y) {
1365 do {
1366 SkASSERT(fWorkEdge.fEdge->fVerbs.begin() < fWorkEdge.fVerb);
1367 fWorkEdge.fPts -= *--fWorkEdge.fVerb;
1368 SkASSERT(fWorkEdge.fEdge->fPts.begin() <= fWorkEdge.fPts);
1369 } while (fWorkEdge.fPts[0].fY >= y);
1370 initT();
caryclark@google.com198e0542012-03-30 18:47:02 +00001371 SkASSERT(!fExplicitTs);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001372 fTIndex = fTs->count() + 1;
1373 }
1374
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001375 void calcLeft(SkScalar y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001376 // OPTIMIZE: put a kDone_Verb at the end of the verb list?
caryclark@google.com4917f172012-03-05 22:01:21 +00001377 if (fDone || fBelow.fY > y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001378 return; // nothing to do; use last
caryclark@google.com4917f172012-03-05 22:01:21 +00001379 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001380 calcLeft();
caryclark@google.com752b60e2012-03-22 21:11:17 +00001381 if (fAbove.fY == fBelow.fY) {
1382 SkDebugf("%s edge=%d fAbove.fY != fBelow.fY %1.9g\n", __FUNCTION__,
1383 ID(), fAbove.fY);
1384 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001385 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001386
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001387 void calcLeft() {
caryclark@google.coma5764232012-03-28 16:20:21 +00001388 void (*xyAtTFunc)(const SkPoint a[], double t, SkPoint* out);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001389 switch (fWorkEdge.verb()) {
caryclark@google.coma5764232012-03-28 16:20:21 +00001390 case SkPath::kLine_Verb:
1391 xyAtTFunc = LineXYAtT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001392 break;
caryclark@google.coma5764232012-03-28 16:20:21 +00001393 case SkPath::kQuad_Verb:
1394 xyAtTFunc = QuadXYAtT;
1395 break;
1396 case SkPath::kCubic_Verb:
1397 xyAtTFunc = CubicXYAtT;
1398 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001399 default:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001400 SkASSERT(0);
1401 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001402 // OPTIMIZATION: if fXAbove, fXBelow have already been computed
1403 // for the fTIndex, don't do it again
1404 // For identical x, this lets us know which edge is first.
1405 // If both edges have T values < 1, check x at next T (fXBelow).
caryclark@google.com198e0542012-03-30 18:47:02 +00001406 int add = (fTIndex <= fTs->count() - fExplicitTs) - 1;
caryclark@google.coma5764232012-03-28 16:20:21 +00001407 double tAbove = t(fTIndex + add);
1408 (*xyAtTFunc)(fWorkEdge.fPts, tAbove, &fAbove);
1409 double tBelow = t(fTIndex - ~add);
1410 (*xyAtTFunc)(fWorkEdge.fPts, tBelow, &fBelow);
1411 SkASSERT(tAbove != tBelow);
1412 // FIXME: this can loop forever
1413 // need a break if we hit the end
caryclark@google.com198e0542012-03-30 18:47:02 +00001414 // FIXME: in unit test, figure out how explicit Ts work as well
caryclark@google.coma5764232012-03-28 16:20:21 +00001415 while (fAbove.fY == fBelow.fY) {
1416 if (add < 0 || fTIndex == fTs->count()) {
1417 add -= 1;
1418 SkASSERT(fTIndex + add >= 0);
1419 tAbove = t(fTIndex + add);
1420 (*xyAtTFunc)(fWorkEdge.fPts, tAbove, &fAbove);
1421 } else {
1422 add += 1;
1423 SkASSERT(fTIndex - ~add <= fTs->count() + 1);
1424 tBelow = t(fTIndex - ~add);
1425 (*xyAtTFunc)(fWorkEdge.fPts, tBelow, &fBelow);
1426 }
1427 }
1428 #if DEBUG_ABOVE_BELOW
1429 fTAbove = tAbove;
1430 fTBelow = tBelow;
1431 #endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00001432 }
1433
caryclark@google.com752b60e2012-03-22 21:11:17 +00001434 bool done(SkScalar bottom) const {
1435 return fDone || fYBottom >= bottom;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001436 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001437
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001438 void fixBelow() {
1439 if (fFixBelow) {
caryclark@google.coma5764232012-03-28 16:20:21 +00001440 switch (fWorkEdge.verb()) {
1441 case SkPath::kLine_Verb:
1442 LineXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1443 break;
1444 case SkPath::kQuad_Verb:
1445 QuadXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1446 break;
1447 case SkPath::kCubic_Verb:
1448 CubicXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1449 break;
1450 default:
1451 SkASSERT(0);
1452 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001453 fFixBelow = false;
1454 }
1455 }
1456
caryclark@google.com6680fb12012-02-07 22:10:51 +00001457 void init(const InEdge* edge) {
1458 fWorkEdge.init(edge);
1459 initT();
caryclark@google.com4917f172012-03-05 22:01:21 +00001460 fBelow.fY = SK_ScalarMin;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001461 fDone = false;
1462 fYBottom = SK_ScalarMin;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001463 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001464
caryclark@google.com6680fb12012-02-07 22:10:51 +00001465 void initT() {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001466 const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
1467 SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
1468 const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
caryclark@google.com198e0542012-03-30 18:47:02 +00001469 fTs = &interceptPtr->fTs;
1470 fExplicitTs = interceptPtr->fExplicit;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001471 // the above is conceptually the same as
1472 // fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
1473 // but templated arrays don't allow returning a pointer to the end() element
caryclark@google.com6680fb12012-02-07 22:10:51 +00001474 fTIndex = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +00001475 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001476
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001477 // OPTIMIZATION: record if two edges are coincident when the are intersected
1478 // It's unclear how to do this -- seems more complicated than recording the
1479 // t values, since the same t values could exist intersecting non-coincident
1480 // edges.
1481 bool isCoincidentWith(const ActiveEdge* edge, SkScalar y) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001482 if (fAbove != edge->fAbove || fBelow != edge->fBelow) {
1483 return false;
1484 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001485 uint8_t verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
1486 uint8_t edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb()
1487 : edge->fWorkEdge.verb();
1488 if (verb != edgeVerb) {
1489 return false;
1490 }
1491 switch (verb) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001492 case SkPath::kLine_Verb: {
caryclark@google.com4917f172012-03-05 22:01:21 +00001493 return true;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001494 }
1495 default:
1496 // FIXME: add support for all curve types
1497 SkASSERT(0);
1498 }
1499 return false;
1500 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001501
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001502 // The shortest close call edge should be moved into a position where
1503 // it contributes if the winding is transitioning to or from zero.
1504 bool swapClose(const ActiveEdge* next, int prev, int wind, int mask) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001505#if DEBUG_ADJUST_COINCIDENT
1506 SkDebugf("%s edge=%d (%g) next=%d (%g) prev=%d wind=%d nextWind=%d\n",
1507 __FUNCTION__, ID(), fBelow.fY, next->ID(), next->fBelow.fY,
1508 prev, wind, wind + next->fWorkEdge.winding());
1509#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001510 if ((prev & mask) == 0 || (wind & mask) == 0) {
1511 return next->fBelow.fY < fBelow.fY;
1512 }
1513 int nextWinding = wind + next->fWorkEdge.winding();
1514 if ((nextWinding & mask) == 0) {
1515 return fBelow.fY < next->fBelow.fY;
1516 }
1517 return false;
1518 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001519
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001520 bool swapCoincident(const ActiveEdge* edge, SkScalar bottom) const {
1521 if (fBelow.fY >= bottom || fDone || edge->fDone) {
1522 return false;
1523 }
1524 ActiveEdge thisWork = *this;
1525 ActiveEdge edgeWork = *edge;
1526 while ((thisWork.advanceT() || thisWork.advance())
1527 && (edgeWork.advanceT() || edgeWork.advance())) {
1528 thisWork.calcLeft();
1529 edgeWork.calcLeft();
1530 if (thisWork < edgeWork) {
1531 return false;
1532 }
1533 if (edgeWork < thisWork) {
1534 return true;
1535 }
1536 }
1537 return false;
1538 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001539
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001540 bool tooCloseToCall(const ActiveEdge* edge) const {
1541 int ulps;
caryclark@google.comd88e0892012-03-27 13:23:51 +00001542 // FIXME: the first variation works better (or at least causes fewer tests
1543 // to fail than the second, although the second's logic better matches the
1544 // current sort criteria. Need to track down the cause of the crash, and
1545 // see if the second case isn't somehow buggy.
1546#if 01
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001547 // FIXME: don't compare points for equality
1548 // OPTIMIZATION: refactor to make one call to UlpsDiff
1549 if (edge->fAbove.fY - fAbove.fY > fBelow.fY - edge->fAbove.fY
1550 && fBelow.fY < edge->fBelow.fY
1551 || fAbove.fY - edge->fAbove.fY < edge->fBelow.fY - fAbove.fY
1552 && edge->fBelow.fY < fBelow.fY) {
1553 const SkPoint& check = edge->fBelow.fY <= fBelow.fY
1554 && fBelow != edge->fBelow ? edge->fBelow :
1555 edge->fAbove;
1556 ulps = UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
1557 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX));
1558 } else {
1559 const SkPoint& check = fBelow.fY <= edge->fBelow.fY
1560 && fBelow != edge->fBelow ? fBelow : fAbove;
1561 ulps = UlpsDiff((edge->fBelow.fY - edge->fAbove.fY)
1562 * (check.fX - edge->fAbove.fX),
1563 (check.fY - edge->fAbove.fY)
1564 * (edge->fBelow.fX - edge->fAbove.fX));
1565 }
caryclark@google.comd88e0892012-03-27 13:23:51 +00001566#else
1567 double t1, t2, b1, b2;
1568 if (edge->fAbove.fY < fAbove.fY) {
1569 t1 = (edge->fBelow.fY - edge->fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
1570 t2 = (fAbove.fY - edge->fAbove.fY) * (edge->fBelow.fX - edge->fAbove.fX);
1571 } else if (edge->fAbove.fY > fAbove.fY) {
1572 t1 = (fBelow.fY - fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
1573 t2 = (fAbove.fY - edge->fAbove.fY) * (fBelow.fX - fAbove.fX);
1574 } else {
1575 t1 = fAbove.fX;
1576 t2 = edge->fAbove.fX;
1577 }
1578 if (edge->fBelow.fY > fBelow.fY) {
1579 b1 = (edge->fBelow.fY - edge->fAbove.fY) * (fBelow.fX - edge->fBelow.fX);
1580 b2 = (fBelow.fY - edge->fBelow.fY) * (edge->fBelow.fX - edge->fAbove.fX);
1581 } else if (edge->fBelow.fY < fBelow.fY) {
1582 b1 = (fBelow.fY - fAbove.fY) * (fBelow.fX - edge->fBelow.fX);
1583 b2 = (fBelow.fY - edge->fBelow.fY) * (fBelow.fX - fAbove.fX);
1584 } else {
1585 b1 = fBelow.fX;
1586 b2 = edge->fBelow.fX;
1587 }
1588 if (fabs(t1 - t2) > fabs(b1 - b2)) {
1589 ulps = UlpsDiff(t1, t2);
1590 } else {
1591 ulps = UlpsDiff(b1, b2);
1592 }
1593#if DEBUG_ADJUST_COINCIDENT
1594 SkDebugf("%s this=%d edge=%d ulps=%d\n", __FUNCTION__, ID(), edge->ID(),
1595 ulps);
1596#endif
1597#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001598 return ulps >= 0 && ulps <= 32;
1599 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001600
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001601 double nextT() const {
caryclark@google.com198e0542012-03-30 18:47:02 +00001602 SkASSERT(fTIndex <= fTs->count() - fExplicitTs);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001603 return t(fTIndex + 1);
1604 }
caryclark@google.com6680fb12012-02-07 22:10:51 +00001605
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001606 double t() const {
caryclark@google.com198e0542012-03-30 18:47:02 +00001607 return t(fTIndex);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001608 }
1609
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001610 double t(int tIndex) const {
caryclark@google.com198e0542012-03-30 18:47:02 +00001611 if (fExplicitTs) {
1612 SkASSERT(tIndex < fTs->count());
1613 return (*fTs)[tIndex];
1614 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001615 if (tIndex == 0) {
1616 return 0;
1617 }
1618 if (tIndex > fTs->count()) {
1619 return 1;
1620 }
1621 return (*fTs)[tIndex - 1];
1622 }
1623
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001624 // FIXME: debugging only
caryclark@google.com752b60e2012-03-22 21:11:17 +00001625 int ID() const {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001626 return fWorkEdge.fEdge->fID;
1627 }
1628
1629public:
caryclark@google.com6680fb12012-02-07 22:10:51 +00001630 WorkEdge fWorkEdge;
1631 const SkTDArray<double>* fTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001632 SkPoint fAbove;
1633 SkPoint fBelow;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001634#if DEBUG_ABOVE_BELOW
1635 double fTAbove;
1636 double fTBelow;
1637#endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001638 SkScalar fYBottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001639 int fCoincident;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001640 int fTIndex;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001641 bool fSkip; // OPTIMIZATION: use bitfields?
1642 bool fCloseCall;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001643 bool fDone;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001644 bool fFixBelow;
caryclark@google.com198e0542012-03-30 18:47:02 +00001645 bool fExplicitTs;
caryclark@google.comc6825902012-02-03 22:07:47 +00001646};
1647
caryclark@google.com6680fb12012-02-07 22:10:51 +00001648static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001649 size_t count = activeEdges.count();
1650 for (size_t index = 0; index < count; ++index) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001651 if (edge == activeEdges[index].fWorkEdge.fEdge) {
1652 return;
1653 }
1654 }
1655 ActiveEdge* active = activeEdges.append();
1656 active->init(edge);
1657}
1658
caryclark@google.com4917f172012-03-05 22:01:21 +00001659// Find any intersections in the range of active edges. A pair of edges, on
1660// either side of another edge, may change the winding contribution for part of
1661// the edge.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001662// Keep horizontal edges just for
caryclark@google.com4917f172012-03-05 22:01:21 +00001663// the purpose of computing when edges change their winding contribution, since
1664// this is essentially computing the horizontal intersection.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001665static void addBottomT(InEdge** currentPtr, InEdge** lastPtr,
1666 HorizontalEdge** horizontal) {
1667 InEdge** testPtr = currentPtr - 1;
1668 HorizontalEdge* horzEdge = *horizontal;
1669 SkScalar left = horzEdge->fLeft;
1670 SkScalar bottom = horzEdge->fY;
1671 while (++testPtr != lastPtr) {
1672 InEdge* test = *testPtr;
1673 if (test->fBounds.fBottom <= bottom || test->fBounds.fRight <= left) {
1674 continue;
1675 }
1676 WorkEdge wt;
1677 wt.init(test);
1678 do {
1679 HorizontalEdge** sorted = horizontal;
1680 horzEdge = *sorted;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001681 do {
caryclark@google.com198e0542012-03-30 18:47:02 +00001682 double wtTs[4];
1683 int pts;
1684 uint8_t verb = wt.verb();
1685 switch (verb) {
1686 case SkPath::kLine_Verb:
1687 pts = LineIntersect(wt.fPts, horzEdge->fLeft,
1688 horzEdge->fRight, horzEdge->fY, wtTs);
1689 break;
1690 case SkPath::kQuad_Verb:
1691 pts = QuadIntersect(wt.fPts, horzEdge->fLeft,
1692 horzEdge->fRight, horzEdge->fY, wtTs);
1693 break;
1694 case SkPath::kCubic_Verb:
1695 pts = CubicIntersect(wt.fPts, horzEdge->fLeft,
1696 horzEdge->fRight, horzEdge->fY, wtTs);
1697 break;
1698 }
1699 if (pts) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001700#if DEBUG_ADD_BOTTOM_TS
caryclark@google.com198e0542012-03-30 18:47:02 +00001701 for (int x = 0; x < pts; ++x) {
1702 SkDebugf("%s y=%g wtTs[0]=%g (%g,%g", __FUNCTION__,
1703 horzEdge->fY, wtTs[x], wt.fPts[0].fX, wt.fPts[0].fY);
1704 for (int y = 0; y < verb; ++y) {
1705 SkDebugf(" %g,%g", wt.fPts[y + 1].fX, wt.fPts[y + 1].fY));
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001706 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001707 SkDebugf(")\n");
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001708 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001709 if (pts > verb) {
1710 SkASSERT(0); // FIXME ? should this work?
1711 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1712 }
1713#endif
1714 test->add(wtTs, pts, wt.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001715 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001716 horzEdge = *++sorted;
1717 } while (horzEdge->fY == bottom
1718 && horzEdge->fLeft <= test->fBounds.fRight);
1719 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001720 }
1721}
1722
caryclark@google.coma5764232012-03-28 16:20:21 +00001723static void debugShowLineIntersection(int pts, const WorkEdge& wt,
1724 const WorkEdge& wn, const double wtTs[2], const double wnTs[2]) {
1725#if DEBUG_ADD_INTERSECTING_TS
1726 if (!pts) {
1727 return;
1728 }
1729 SkPoint wtOutPt, wnOutPt;
1730 LineXYAtT(wt.fPts, wtTs[0], &wtOutPt);
1731 LineXYAtT(wn.fPts, wnTs[0], &wnOutPt);
1732 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
1733 __FUNCTION__,
1734 wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
1735 wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY,
1736 test->fID, next->fID);
1737 if (pts == 2) {
1738 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1739 }
1740 SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
1741 __FUNCTION__,
1742 wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY,
1743 wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY,
1744 test->fID, next->fID);
1745 if (pts == 2) {
1746 SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
1747 }
1748#endif
1749}
1750
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001751static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001752 InEdge** testPtr = currentPtr - 1;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001753 // FIXME: lastPtr should be past the point of interest, so
1754 // test below should be lastPtr - 2
1755 // that breaks testSimplifyTriangle22, so further investigation is needed
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001756 while (++testPtr != lastPtr - 1) {
1757 InEdge* test = *testPtr;
1758 InEdge** nextPtr = testPtr;
1759 do {
1760 InEdge* next = *++nextPtr;
caryclark@google.com198e0542012-03-30 18:47:02 +00001761 // FIXME: this compares against the sentinel sometimes
1762 // OPTIMIZATION: this may never be needed since this gets called
1763 // in two passes now. Verify that double hits are appropriate.
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001764 if (test->cached(next)) {
1765 continue;
1766 }
1767 if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
1768 continue;
1769 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001770 WorkEdge wt, wn;
1771 wt.init(test);
1772 wn.init(next);
1773 do {
caryclark@google.coma5764232012-03-28 16:20:21 +00001774 int pts;
1775 Intersections ts;
1776 bool swap = false;
1777 switch (wt.verb()) {
1778 case SkPath::kLine_Verb:
1779 switch (wn.verb()) {
1780 case SkPath::kLine_Verb: {
1781 pts = LineIntersect(wt.fPts, wn.fPts, ts);
1782 debugShowLineIntersection(pts, wt, wn,
1783 ts.fT[0], ts.fT[1]);
1784 break;
1785 }
1786 case SkPath::kQuad_Verb: {
1787 swap = true;
1788 pts = QuadLineIntersect(wn.fPts, wt.fPts, ts);
1789 break;
1790 }
1791 case SkPath::kCubic_Verb: {
1792 swap = true;
1793 pts = CubicLineIntersect(wn.fPts, wt.fPts, ts);
1794 break;
1795 }
1796 default:
1797 SkASSERT(0);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001798 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001799 break;
1800 case SkPath::kQuad_Verb:
1801 switch (wn.verb()) {
1802 case SkPath::kLine_Verb: {
1803 pts = QuadLineIntersect(wt.fPts, wn.fPts, ts);
1804 break;
1805 }
1806 case SkPath::kQuad_Verb: {
1807 pts = QuadIntersect(wt.fPts, wn.fPts, ts);
1808 break;
1809 }
1810 case SkPath::kCubic_Verb: {
1811 // FIXME: promote quad to cubic
1812 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
1813 break;
1814 }
1815 default:
1816 SkASSERT(0);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001817 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001818 break;
1819 case SkPath::kCubic_Verb:
1820 switch (wn.verb()) {
1821 case SkPath::kLine_Verb: {
1822 pts = CubicLineIntersect(wt.fPts, wn.fPts, ts);
1823 break;
1824 }
1825 case SkPath::kQuad_Verb: {
1826 // FIXME: promote quad to cubic
1827 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
1828 break;
1829 }
1830 case SkPath::kCubic_Verb: {
1831 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
1832 break;
1833 }
1834 default:
1835 SkASSERT(0);
1836 }
1837 break;
1838 default:
1839 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001840 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001841 test->add(ts.fT[swap], pts, wt.verbIndex());
1842 next->add(ts.fT[!swap], pts, wn.verbIndex());
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001843 } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
1844 } while (nextPtr != lastPtr);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001845 }
1846}
1847
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001848static InEdge** advanceEdges(SkTDArray<ActiveEdge>* activeEdges,
caryclark@google.com6008c6562012-02-15 22:01:16 +00001849 InEdge** currentPtr, InEdge** lastPtr, SkScalar y) {
1850 InEdge** testPtr = currentPtr - 1;
1851 while (++testPtr != lastPtr) {
1852 if ((*testPtr)->fBounds.fBottom > y) {
1853 continue;
1854 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001855 if (activeEdges) {
1856 InEdge* test = *testPtr;
1857 ActiveEdge* activePtr = activeEdges->begin() - 1;
1858 ActiveEdge* lastActive = activeEdges->end();
1859 while (++activePtr != lastActive) {
1860 if (activePtr->fWorkEdge.fEdge == test) {
1861 activeEdges->remove(activePtr - activeEdges->begin());
1862 break;
1863 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001864 }
1865 }
1866 if (testPtr == currentPtr) {
1867 ++currentPtr;
1868 }
1869 }
1870 return currentPtr;
1871}
1872
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001873// OPTIMIZE: inline?
1874static HorizontalEdge** advanceHorizontal(HorizontalEdge** edge, SkScalar y) {
1875 while ((*edge)->fY < y) {
1876 ++edge;
1877 }
1878 return edge;
1879}
1880
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001881// compute bottom taking into account any intersected edges
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001882static SkScalar computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
1883 SkScalar y, SkScalar bottom) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001884 ActiveEdge* activePtr = activeEdges.begin() - 1;
1885 ActiveEdge* lastActive = activeEdges.end();
1886 while (++activePtr != lastActive) {
1887 const InEdge* test = activePtr->fWorkEdge.fEdge;
1888 if (!test->fContainsIntercepts) {
1889 continue;
1890 }
1891 WorkEdge wt;
1892 wt.init(test);
1893 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001894 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
caryclark@google.com4917f172012-03-05 22:01:21 +00001895 if (intercepts.fTopIntercepts > 1) {
1896 SkScalar yTop = wt.fPts[0].fY;
1897 if (yTop > y && bottom > yTop) {
1898 bottom = yTop;
1899 }
1900 }
1901 if (intercepts.fBottomIntercepts > 1) {
1902 SkScalar yBottom = wt.fPts[wt.verb()].fY;
1903 if (yBottom > y && bottom > yBottom) {
1904 bottom = yBottom;
1905 }
1906 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001907 const SkTDArray<double>& fTs = intercepts.fTs;
1908 size_t count = fTs.count();
1909 for (size_t index = 0; index < count; ++index) {
caryclark@google.coma5764232012-03-28 16:20:21 +00001910 SkScalar yIntercept;
1911 switch (wt.verb()) {
1912 case SkPath::kLine_Verb: {
1913 yIntercept = LineYAtT(wt.fPts, fTs[index]);
1914 break;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001915 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001916 case SkPath::kQuad_Verb: {
1917 yIntercept = QuadYAtT(wt.fPts, fTs[index]);
1918 break;
1919 }
1920 case SkPath::kCubic_Verb: {
1921 yIntercept = CubicYAtT(wt.fPts, fTs[index]);
1922 break;
1923 }
1924 default:
1925 SkASSERT(0); // should never get here
1926 }
1927 if (yIntercept > y && bottom > yIntercept) {
1928 bottom = yIntercept;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001929 }
1930 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001931 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001932 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001933#if DEBUG_BOTTOM
1934 SkDebugf("%s bottom=%1.9g\n", __FUNCTION__, bottom);
1935#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001936 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001937}
1938
1939static SkScalar findBottom(InEdge** currentPtr,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001940 InEdge** edgeListEnd, SkTDArray<ActiveEdge>* activeEdges, SkScalar y,
caryclark@google.com6008c6562012-02-15 22:01:16 +00001941 bool asFill, InEdge**& testPtr) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001942 InEdge* current = *currentPtr;
1943 SkScalar bottom = current->fBounds.fBottom;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001944
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001945 // find the list of edges that cross y
caryclark@google.com6008c6562012-02-15 22:01:16 +00001946 InEdge* test = *testPtr;
1947 while (testPtr != edgeListEnd) {
1948 SkScalar testTop = test->fBounds.fTop;
1949 if (bottom <= testTop) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001950 break;
1951 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001952 SkScalar testBottom = test->fBounds.fBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001953 // OPTIMIZATION: Shortening the bottom is only interesting when filling
1954 // and when the edge is to the left of a longer edge. If it's a framing
1955 // edge, or part of the right, it won't effect the longer edges.
caryclark@google.com6008c6562012-02-15 22:01:16 +00001956 if (testTop > y) {
1957 bottom = testTop;
1958 break;
1959 }
1960 if (y < testBottom) {
1961 if (bottom > testBottom) {
1962 bottom = testBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001963 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001964 if (activeEdges) {
1965 addToActive(*activeEdges, test);
1966 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001967 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001968 test = *++testPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001969 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001970#if DEBUG_BOTTOM
1971 SkDebugf("%s %d bottom=%1.9g\n", __FUNCTION__, activeEdges ? 2 : 1, bottom);
1972#endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001973 return bottom;
1974}
1975
1976static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
1977 SkTDArray<InEdge*>& edgeList) {
1978 size_t edgeCount = edges.count();
1979 if (edgeCount == 0) {
1980 return;
1981 }
1982 for (size_t index = 0; index < edgeCount; ++index) {
1983 *edgeList.append() = &edges[index];
1984 }
1985 edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1986 *edgeList.append() = &edgeSentinel;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001987 QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001988}
1989
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001990static void makeHorizontalList(SkTDArray<HorizontalEdge>& edges,
1991 HorizontalEdge& edgeSentinel, SkTDArray<HorizontalEdge*>& edgeList) {
1992 size_t edgeCount = edges.count();
1993 if (edgeCount == 0) {
1994 return;
1995 }
1996 for (size_t index = 0; index < edgeCount; ++index) {
1997 *edgeList.append() = &edges[index];
1998 }
1999 edgeSentinel.fLeft = edgeSentinel.fRight = edgeSentinel.fY = SK_ScalarMax;
2000 *edgeList.append() = &edgeSentinel;
2001 QSort<HorizontalEdge>(edgeList.begin(), edgeList.end() - 1);
2002}
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002003
2004static void skipCoincidence(int lastWinding, int winding, int windingMask,
2005 ActiveEdge* activePtr, ActiveEdge* firstCoincident) {
2006 if (((lastWinding & windingMask) == 0) ^ (winding & windingMask) != 0) {
2007 return;
2008 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002009 // FIXME: ? shouldn't this be if (lastWinding & windingMask) ?
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002010 if (lastWinding) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002011#if DEBUG_ADJUST_COINCIDENT
2012 SkDebugf("%s edge=%d 1 set skip=false\n", __FUNCTION__, activePtr->ID());
2013#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002014 activePtr->fSkip = false;
2015 } else {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002016#if DEBUG_ADJUST_COINCIDENT
2017 SkDebugf("%s edge=%d 2 set skip=false\n", __FUNCTION__, firstCoincident->ID());
2018#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002019 firstCoincident->fSkip = false;
2020 }
2021}
2022
caryclark@google.com6008c6562012-02-15 22:01:16 +00002023static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002024 SkTDArray<ActiveEdge*>& edgeList, SkScalar y) {
caryclark@google.com6008c6562012-02-15 22:01:16 +00002025 size_t edgeCount = activeEdges.count();
2026 if (edgeCount == 0) {
2027 return;
2028 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002029#if DEBUG_SORT_HORIZONTAL
caryclark@google.comd88e0892012-03-27 13:23:51 +00002030 const int tab = 3; // FIXME: debugging only
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002031 SkDebugf("%s y=%1.9g\n", __FUNCTION__, y);
2032#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00002033 size_t index;
2034 for (index = 0; index < edgeCount; ++index) {
2035 ActiveEdge& activeEdge = activeEdges[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002036 do {
2037 activeEdge.calcLeft(y);
2038 // skip segments that don't span y
2039 if (activeEdge.fAbove != activeEdge.fBelow) {
2040 break;
2041 }
2042 if (activeEdge.fDone) {
2043#if DEBUG_SORT_HORIZONTAL
2044 SkDebugf("%*s edge=%d done\n", tab, "", activeEdge.ID());
2045#endif
2046 goto nextEdge;
2047 }
2048#if DEBUG_SORT_HORIZONTAL
2049 SkDebugf("%*s edge=%d above==below\n", tab, "", activeEdge.ID());
2050#endif
2051 } while (activeEdge.advanceT() || activeEdge.advance());
2052#if DEBUG_SORT_HORIZONTAL
2053 SkDebugf("%*s edge=%d above=(%1.9g,%1.9g) (%1.9g) below=(%1.9g,%1.9g)"
2054 " (%1.9g)\n", tab, "", activeEdge.ID(),
2055 activeEdge.fAbove.fX, activeEdge.fAbove.fY, activeEdge.fTAbove,
2056 activeEdge.fBelow.fX, activeEdge.fBelow.fY, activeEdge.fTBelow);
2057#endif
2058 activeEdge.fSkip = activeEdge.fCloseCall = activeEdge.fFixBelow = false;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002059 *edgeList.append() = &activeEdge;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002060nextEdge:
2061 ;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002062 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002063 QSort<ActiveEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002064}
2065
2066// remove coincident edges
2067// OPTIMIZE: remove edges? This is tricky because the current logic expects
2068// the winding count to be maintained while skipping coincident edges. In
2069// addition to removing the coincident edges, the remaining edges would need
2070// to have a different winding value, possibly different per intercept span.
2071static SkScalar adjustCoincident(SkTDArray<ActiveEdge*>& edgeList,
2072 int windingMask, SkScalar y, SkScalar bottom, OutEdgeBuilder& outBuilder)
2073{
2074#if DEBUG_ADJUST_COINCIDENT
2075 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
2076#endif
2077 size_t edgeCount = edgeList.count();
2078 if (edgeCount == 0) {
2079 return bottom;
2080 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002081 ActiveEdge* activePtr = edgeList[0];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002082 size_t index;
2083 bool foundCoincident = false;
2084 int firstIndex = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002085 for (index = 1; index < edgeCount; ++index) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002086 ActiveEdge* nextPtr = edgeList[index];
2087 bool closeCall = false;
2088 activePtr->fCoincident = firstIndex;
2089 if (activePtr->isCoincidentWith(nextPtr, y)
2090 || (closeCall = activePtr->tooCloseToCall(nextPtr))) {
2091 activePtr->fSkip = nextPtr->fSkip = foundCoincident = true;
2092 activePtr->fCloseCall = nextPtr->fCloseCall = closeCall;
2093 } else {
2094 firstIndex = index;
2095 }
2096 activePtr = nextPtr;
2097 }
2098 activePtr->fCoincident = firstIndex;
2099 if (!foundCoincident) {
2100 return bottom;
2101 }
2102 int winding = 0;
2103 activePtr = edgeList[0];
2104 for (index = 1; index < edgeCount; ++index) {
2105 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002106 winding += activePtr->fWorkEdge.winding();
caryclark@google.com6008c6562012-02-15 22:01:16 +00002107 ActiveEdge* nextPtr = edgeList[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002108 if (activePtr->fCoincident == nextPtr->fCoincident) {
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002109 // the coincident edges may not have been sorted above -- advance
2110 // the edges and resort if needed
2111 // OPTIMIZE: if sorting is done incrementally as new edges are added
2112 // and not all at once as is done here, fold this test into the
2113 // current less than test.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002114 if (activePtr->fCloseCall ? activePtr->swapClose(nextPtr,
2115 priorWinding, winding, windingMask)
2116 : activePtr->swapCoincident(nextPtr, bottom)) {
caryclark@google.com4917f172012-03-05 22:01:21 +00002117 winding -= activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002118 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
2119 SkTSwap<ActiveEdge*>(activePtr, nextPtr);
caryclark@google.com4917f172012-03-05 22:01:21 +00002120 winding += activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002121 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002122 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002123 activePtr = nextPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002124 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002125 int firstCoincidentWinding = 0;
2126 ActiveEdge* firstCoincident = NULL;
2127 winding = 0;
2128 activePtr = edgeList[0];
2129 for (index = 1; index < edgeCount; ++index) {
2130 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002131 winding += activePtr->fWorkEdge.winding();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002132 ActiveEdge* nextPtr = edgeList[index];
2133 if (activePtr->fCoincident == nextPtr->fCoincident) {
2134 if (!firstCoincident) {
2135 firstCoincident = activePtr;
2136 firstCoincidentWinding = priorWinding;
2137 }
2138 if (activePtr->fCloseCall) {
2139 // If one of the edges has already been added to out as a non
2140 // coincident edge, trim it back to the top of this span
2141 if (outBuilder.trimLine(y, activePtr->ID())) {
2142 activePtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002143 #if DEBUG_ADJUST_COINCIDENT
2144 SkDebugf("%s 1 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
2145 __FUNCTION__, activePtr->ID(), y, activePtr->fYBottom);
2146 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002147 activePtr->fYBottom = y;
2148 }
2149 if (outBuilder.trimLine(y, nextPtr->ID())) {
2150 nextPtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002151 #if DEBUG_ADJUST_COINCIDENT
2152 SkDebugf("%s 2 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
2153 __FUNCTION__, nextPtr->ID(), y, nextPtr->fYBottom);
2154 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002155 nextPtr->fYBottom = y;
2156 }
2157 // add missing t values so edges can be the same length
2158 SkScalar testY = activePtr->fBelow.fY;
2159 nextPtr->addTatYBelow(testY);
2160 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002161 #if DEBUG_ADJUST_COINCIDENT
2162 SkDebugf("%s 3 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
2163 __FUNCTION__, activePtr->ID(), testY, bottom);
2164 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002165 bottom = testY;
2166 }
2167 testY = nextPtr->fBelow.fY;
2168 activePtr->addTatYBelow(testY);
2169 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002170 #if DEBUG_ADJUST_COINCIDENT
2171 SkDebugf("%s 4 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
2172 __FUNCTION__, nextPtr->ID(), testY, bottom);
2173 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002174 bottom = testY;
2175 }
2176 }
2177 } else if (firstCoincident) {
2178 skipCoincidence(firstCoincidentWinding, winding, windingMask,
2179 activePtr, firstCoincident);
2180 firstCoincident = NULL;
2181 }
2182 activePtr = nextPtr;
2183 }
2184 if (firstCoincident) {
2185 winding += activePtr->fWorkEdge.winding();
2186 skipCoincidence(firstCoincidentWinding, winding, windingMask, activePtr,
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002187 firstCoincident);
2188 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002189 // fix up the bottom for close call edges. OPTIMIZATION: maybe this could
2190 // be in the loop above, but moved here since loop above reads fBelow and
2191 // it felt unsafe to write it in that loop
2192 for (index = 0; index < edgeCount; ++index) {
2193 (edgeList[index])->fixBelow();
2194 }
2195 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002196}
2197
2198// stitch edge and t range that satisfies operation
caryclark@google.com6008c6562012-02-15 22:01:16 +00002199static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
caryclark@google.com752b60e2012-03-22 21:11:17 +00002200 SkScalar bottom, int windingMask, bool fill, OutEdgeBuilder& outBuilder) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002201 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002202 ActiveEdge** activeHandle = edgeList.begin() - 1;
2203 ActiveEdge** lastActive = edgeList.end();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002204 const int tab = 7; // FIXME: debugging only
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002205 if (gShowDebugf) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002206 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002207 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002208 while (++activeHandle != lastActive) {
2209 ActiveEdge* activePtr = *activeHandle;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002210 const WorkEdge& wt = activePtr->fWorkEdge;
2211 int lastWinding = winding;
2212 winding += wt.winding();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002213 if (gShowDebugf) {
2214 SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d"
2215#if DEBUG_ABOVE_BELOW
2216 " above=%1.9g below=%1.9g"
2217#endif
2218 "\n",
2219 tab-4, "", activePtr->ID(), lastWinding,
2220 winding, activePtr->fSkip, activePtr->fCloseCall
2221#if DEBUG_ABOVE_BELOW
2222 , activePtr->fTAbove, activePtr->fTBelow
2223#endif
2224 );
2225 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00002226 if (activePtr->done(bottom)) {
2227 if (gShowDebugf) {
2228 SkDebugf("%*s fDone=%d || fYBottom=%1.9g >= bottom\n", tab, "",
2229 activePtr->fDone, activePtr->fYBottom);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002230 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00002231 continue;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002232 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00002233 int opener = (lastWinding & windingMask) == 0;
2234 bool closer = (winding & windingMask) == 0;
2235 SkASSERT(!opener | !closer);
2236 bool inWinding = opener | closer;
caryclark@google.coma5764232012-03-28 16:20:21 +00002237 SkPoint clippedPts[4];
caryclark@google.com4917f172012-03-05 22:01:21 +00002238 const SkPoint* clipped = NULL;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002239 uint8_t verb = wt.verb();
2240 bool moreToDo, aboveBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002241 do {
2242 double currentT = activePtr->t();
caryclark@google.com6008c6562012-02-15 22:01:16 +00002243 SkASSERT(currentT < 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002244 const SkPoint* points = wt.fPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002245 double nextT;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002246 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002247 nextT = activePtr->nextT();
caryclark@google.coma5764232012-03-28 16:20:21 +00002248 // FIXME: obtuse: want efficient way to say
2249 // !currentT && currentT != 1 || !nextT && nextT != 1
2250 if (currentT * nextT != 0 || currentT + nextT != 1) {
2251 // OPTIMIZATION: if !inWinding, we only need
2252 // clipped[1].fY
2253 switch (verb) {
2254 case SkPath::kLine_Verb:
2255 LineSubDivide(points, currentT, nextT, clippedPts);
2256 break;
2257 case SkPath::kQuad_Verb:
2258 QuadSubDivide(points, currentT, nextT, clippedPts);
2259 break;
2260 case SkPath::kCubic_Verb:
2261 CubicSubDivide(points, currentT, nextT, clippedPts);
2262 break;
2263 default:
2264 SkASSERT(0);
2265 break;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002266 }
caryclark@google.coma5764232012-03-28 16:20:21 +00002267 clipped = clippedPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002268 } else {
caryclark@google.coma5764232012-03-28 16:20:21 +00002269 clipped = points;
2270 }
2271 if (inWinding && !activePtr->fSkip && (fill ? clipped[0].fY
2272 != clipped[verb].fY : clipped[0] != clipped[verb])) {
2273 if (gShowDebugf) {
2274 const char* verbStr[] = {"", "Line", "Quad", "Cubic"};
2275 SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d"
2276 " v=%d t=(%1.9g,%1.9g)\n", tab, "",
2277 verbStr[verb], clipped[0].fX, clipped[0].fY,
2278 clipped[verb].fX, clipped[verb].fY,
2279 activePtr->ID(),
2280 activePtr->fWorkEdge.fVerb
2281 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
2282 currentT, nextT);
2283 }
2284 outBuilder.addCurve(clipped, (SkPath::Verb) verb,
2285 activePtr->fWorkEdge.fEdge->fID,
2286 activePtr->fCloseCall);
2287 } else {
2288 if (gShowDebugf ) {
2289 const char* verbStr[] = {"", "Line", "Quad", "Cubic"};
2290 SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g"
2291 " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
2292 verbStr[verb], clipped[0].fX, clipped[0].fY,
2293 clipped[verb].fX, clipped[verb].fY,
2294 activePtr->ID(),
2295 activePtr->fWorkEdge.fVerb
2296 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
2297 currentT, nextT);
2298 }
2299 }
2300 // by advancing fAbove/fBelow, the next call to sortHorizontal
2301 // will use these values if they're still valid instead of
2302 // recomputing
2303 if (clipped[1].fY > activePtr->fBelow.fY
2304 && bottom >= activePtr->fBelow.fY ) {
2305 activePtr->fAbove = activePtr->fBelow;
2306 activePtr->fBelow = clipped[1];
2307 #if DEBUG_ABOVE_BELOW
2308 activePtr->fTAbove = activePtr->fTBelow;
2309 activePtr->fTBelow = nextT;
2310 #endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002311 }
2312 currentT = nextT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002313 moreToDo = activePtr->advanceT();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002314 activePtr->fYBottom = clipped[verb].fY; // was activePtr->fCloseCall ? bottom :
2315
2316 // clearing the fSkip/fCloseCall bit here means that trailing edges
2317 // fall out of sync, if one edge is long and another is a series of short pieces
2318 // if fSkip/fCloseCall is set, need to recompute coincidence/too-close-to-call
2319 // after advancing
2320 // another approach would be to restrict bottom to smaller part of close call
2321 // maybe this is already happening with coincidence when intersection is computed,
2322 // and needs to be added to the close call computation as well
2323 // this is hard to do because that the bottom is important is not known when
2324 // the lines are intersected; only when the computation for edge sorting is done
2325 // does the need for new bottoms become apparent.
2326 // maybe this is good incentive to scrap the current sort and do an insertion
2327 // sort that can take this into consideration when the x value is computed
2328
2329 // FIXME: initialized in sortHorizontal, cleared here as well so
2330 // that next edge is not skipped -- but should skipped edges ever
2331 // continue? (probably not)
caryclark@google.com752b60e2012-03-22 21:11:17 +00002332 aboveBottom = clipped[verb].fY < bottom;
2333 if (clipped[0].fY != clipped[verb].fY) {
2334 activePtr->fSkip = false;
2335 activePtr->fCloseCall = false;
2336 aboveBottom &= !activePtr->fCloseCall;
2337 } else {
2338 if (activePtr->fSkip || activePtr->fCloseCall) {
caryclark@google.com198e0542012-03-30 18:47:02 +00002339 if (gShowDebugf) SkDebugf("== %1.9g\n", clippedPts[0].fY);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002340 }
2341 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002342 } while (moreToDo & aboveBottom);
2343 } while ((moreToDo || activePtr->advance()) & aboveBottom);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002344 }
2345}
2346
caryclark@google.com198e0542012-03-30 18:47:02 +00002347static void dumpEdgeList(const SkTDArray<InEdge*>& edgeList,
2348 const InEdge& edgeSentinel) {
2349#if DEBUG_DUMP
2350 InEdge** debugPtr = edgeList.begin();
2351 do {
2352 (*debugPtr++)->dump();
2353 } while (*debugPtr != &edgeSentinel);
2354#endif
2355}
2356
caryclark@google.comc6825902012-02-03 22:07:47 +00002357void simplify(const SkPath& path, bool asFill, SkPath& simple) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002358 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
2359 int windingMask = (path.getFillType() & 1) ? 1 : -1;
2360 simple.reset();
2361 simple.setFillType(SkPath::kEvenOdd_FillType);
caryclark@google.comc6825902012-02-03 22:07:47 +00002362 // turn path into list of edges increasing in y
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002363 // if an edge is a quad or a cubic with a y extrema, note it, but leave it
2364 // unbroken. Once we have a list, sort it, then walk the list (walk edges
2365 // twice that have y extrema's on top) and detect crossings -- look for raw
2366 // bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +00002367 SkTArray<InEdge> edges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002368 SkTDArray<HorizontalEdge> horizontalEdges;
2369 InEdgeBuilder builder(path, asFill, edges, horizontalEdges);
caryclark@google.com6680fb12012-02-07 22:10:51 +00002370 SkTDArray<InEdge*> edgeList;
caryclark@google.com6680fb12012-02-07 22:10:51 +00002371 InEdge edgeSentinel;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002372 makeEdgeList(edges, edgeSentinel, edgeList);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002373 SkTDArray<HorizontalEdge*> horizontalList;
2374 HorizontalEdge horizontalSentinel;
2375 makeHorizontalList(horizontalEdges, horizontalSentinel, horizontalList);
caryclark@google.com6680fb12012-02-07 22:10:51 +00002376 InEdge** currentPtr = edgeList.begin();
caryclark@google.com4917f172012-03-05 22:01:21 +00002377 if (!currentPtr) {
2378 return;
2379 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002380 // find all intersections between edges
2381// beyond looking for horizontal intercepts, we need to know if any active edges
2382// intersect edges below 'bottom', but above the active edge segment.
2383// maybe it makes more sense to compute all intercepts before doing anything
2384// else, since the intercept list is long-lived, at least in the current design.
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002385 SkScalar y = (*currentPtr)->fBounds.fTop;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002386 HorizontalEdge** currentHorizontal = horizontalList.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00002387 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +00002388 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002389 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002390 NULL, y, asFill, lastPtr);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002391 if (lastPtr > currentPtr) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002392 if (currentHorizontal) {
2393 if ((*currentHorizontal)->fY < SK_ScalarMax) {
2394 addBottomT(currentPtr, lastPtr, currentHorizontal);
2395 }
2396 currentHorizontal = advanceHorizontal(currentHorizontal, bottom);
2397 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00002398 addIntersectingTs(currentPtr, lastPtr);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002399 }
2400 y = bottom;
2401 currentPtr = advanceEdges(NULL, currentPtr, lastPtr, y);
2402 } while (*currentPtr != &edgeSentinel);
caryclark@google.com198e0542012-03-30 18:47:02 +00002403 // if a quadratic or cubic now has an intermediate T value, see if the Ts
2404 // on either side cause the Y values to monotonically increase. If not, split
2405 // the curve at the new T.
2406 if (builder.containsCurves()) {
2407 currentPtr = edgeList.begin();
2408 SkTArray<InEdge> splits;
2409 do {
2410 (*currentPtr)->splitInflectionPts(splits, builder.nextID());
2411 } while (*++currentPtr != &edgeSentinel);
2412 if (splits.count()) {
2413 edges.pop_back(); // pop the sentinel
2414 for (int index = 0; index < splits.count(); ++index) {
2415 edges.push_back(splits[index]);
2416 }
2417 makeEdgeList(edges, edgeSentinel, edgeList);
2418 }
2419 }
2420 dumpEdgeList(edgeList, edgeSentinel);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002421 // walk the sorted edges from top to bottom, computing accumulated winding
2422 SkTDArray<ActiveEdge> activeEdges;
2423 OutEdgeBuilder outBuilder(asFill);
2424 currentPtr = edgeList.begin();
2425 y = (*currentPtr)->fBounds.fTop;
2426 do {
2427 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
2428 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
2429 &activeEdges, y, asFill, lastPtr);
2430 if (lastPtr > currentPtr) {
2431 bottom = computeInterceptBottom(activeEdges, y, bottom);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002432 SkTDArray<ActiveEdge*> activeEdgeList;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002433 sortHorizontal(activeEdges, activeEdgeList, y);
2434 bottom = adjustCoincident(activeEdgeList, windingMask, y, bottom,
2435 outBuilder);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002436 stitchEdge(activeEdgeList, y, bottom, windingMask, asFill, outBuilder);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002437 }
caryclark@google.comc6825902012-02-03 22:07:47 +00002438 y = bottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002439 // OPTIMIZATION: as edges expire, InEdge allocations could be released
2440 currentPtr = advanceEdges(&activeEdges, currentPtr, lastPtr, y);
caryclark@google.comc6825902012-02-03 22:07:47 +00002441 } while (*currentPtr != &edgeSentinel);
caryclark@google.comc6825902012-02-03 22:07:47 +00002442 // assemble output path from string of pts, verbs
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002443 outBuilder.bridge();
2444 outBuilder.assemble(simple);
caryclark@google.comc6825902012-02-03 22:07:47 +00002445}