blob: f3dff48f97480b6c8cbc6ac54ea906e9dc7e6bd0 [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.comfb173422012-04-10 18:28:55 +000019#if 01 // set to 1 for no debugging whatsoever
caryclark@google.comd88e0892012-03-27 13:23:51 +000020const bool gShowDebugf = false; // FIXME: remove once debugging is complete
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000021
22#define DEBUG_DUMP 0
23#define DEBUG_ADD 0
24#define DEBUG_ADD_INTERSECTING_TS 0
25#define DEBUG_ADD_BOTTOM_TS 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000026#define DEBUG_ABOVE_BELOW 0
27#define DEBUG_ACTIVE_LESS_THAN 0
caryclark@google.comfb173422012-04-10 18:28:55 +000028#define DEBUG_ASSEMBLE 0
29#define DEBUG_BRIDGE 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000030#define DEBUG_SORT_HORIZONTAL 0
31#define DEBUG_OUT 0
32#define DEBUG_OUT_LESS_THAN 0
33#define DEBUG_ADJUST_COINCIDENT 0
caryclark@google.com752b60e2012-03-22 21:11:17 +000034#define DEBUG_BOTTOM 0
caryclark@google.com198e0542012-03-30 18:47:02 +000035#define DEBUG_SPLIT 0
caryclark@google.comfb173422012-04-10 18:28:55 +000036#define DEBUG_STITCH_EDGE 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000037#else
caryclark@google.comd88e0892012-03-27 13:23:51 +000038const bool gShowDebugf = true; // FIXME: remove once debugging is complete
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000039
40#define DEBUG_DUMP 01
41#define DEBUG_ADD 01
42#define DEBUG_ADD_INTERSECTING_TS 0
43#define DEBUG_ADD_BOTTOM_TS 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000044#define DEBUG_ABOVE_BELOW 01
caryclark@google.coma5764232012-03-28 16:20:21 +000045#define DEBUG_ACTIVE_LESS_THAN 0
caryclark@google.comfb173422012-04-10 18:28:55 +000046#define DEBUG_ASSEMBLE 1
47#define DEBUG_BRIDGE 1
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000048#define DEBUG_SORT_HORIZONTAL 01
49#define DEBUG_OUT 01
50#define DEBUG_OUT_LESS_THAN 0
51#define DEBUG_ADJUST_COINCIDENT 1
caryclark@google.com198e0542012-03-30 18:47:02 +000052#define DEBUG_BOTTOM 0
53#define DEBUG_SPLIT 1
caryclark@google.comfb173422012-04-10 18:28:55 +000054#define DEBUG_STITCH_EDGE 1
caryclark@google.com752b60e2012-03-22 21:11:17 +000055
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000056#endif
57
caryclark@google.comfb173422012-04-10 18:28:55 +000058#if DEBUG_ASSEMBLE || DEBUG_BRIDGE
59static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
60#endif
61#if DEBUG_STITCH_EDGE
62static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
63#endif
64
caryclark@google.com6680fb12012-02-07 22:10:51 +000065static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
caryclark@google.coma5764232012-03-28 16:20:21 +000066 Intersections& intersections) {
67 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
68 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
69 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
70}
71
72static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
73 Intersections& intersections) {
74 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
75 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +000076 intersect(aQuad, bLine, intersections);
77 return intersections.fUsed;
caryclark@google.coma5764232012-03-28 16:20:21 +000078}
79
80static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
81 Intersections& intersections) {
82 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
83 {a[3].fX, a[3].fY}};
84 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +000085 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
caryclark@google.coma5764232012-03-28 16:20:21 +000086}
87
88static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
89 Intersections& intersections) {
90 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
91 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 +000092 intersect(aQuad, bQuad, intersections);
93 return intersections.fUsed;
caryclark@google.coma5764232012-03-28 16:20:21 +000094}
95
96static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
97 Intersections& intersections) {
98 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
99 {a[3].fX, a[3].fY}};
100 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
101 {b[3].fX, b[3].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +0000102 intersect(aCubic, bCubic, intersections);
103 return intersections.fUsed;
caryclark@google.comc6825902012-02-03 22:07:47 +0000104}
105
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000106static int LineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
107 SkScalar y, double aRange[2]) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000108 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000109 return horizontalLineIntersect(aLine, left, right, y, aRange);
caryclark@google.comc6825902012-02-03 22:07:47 +0000110}
111
caryclark@google.com198e0542012-03-30 18:47:02 +0000112static int QuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
113 SkScalar y, double aRange[3]) {
114 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
115 return horizontalIntersect(aQuad, left, right, y, aRange);
116}
117
118static int CubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
119 SkScalar y, double aRange[4]) {
120 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
121 {a[3].fX, a[3].fY}};
122 return horizontalIntersect(aCubic, left, right, y, aRange);
123}
124
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000125static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000126 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000127 double x, y;
caryclark@google.coma5764232012-03-28 16:20:21 +0000128 xy_at_t(line, t, x, y);
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000129 out->fX = SkDoubleToScalar(x);
130 out->fY = SkDoubleToScalar(y);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000131}
132
caryclark@google.coma5764232012-03-28 16:20:21 +0000133static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
134 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
135 double x, y;
136 xy_at_t(quad, t, x, y);
137 out->fX = SkDoubleToScalar(x);
138 out->fY = SkDoubleToScalar(y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000139}
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000140
caryclark@google.coma5764232012-03-28 16:20:21 +0000141static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
142 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
143 {a[3].fX, a[3].fY}};
144 double x, y;
145 xy_at_t(cubic, t, x, y);
146 out->fX = SkDoubleToScalar(x);
147 out->fY = SkDoubleToScalar(y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000148}
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000149
caryclark@google.com6680fb12012-02-07 22:10:51 +0000150static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000151 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com6680fb12012-02-07 22:10:51 +0000152 double y;
153 xy_at_t(aLine, t, *(double*) 0, y);
154 return SkDoubleToScalar(y);
155}
156
caryclark@google.coma5764232012-03-28 16:20:21 +0000157static SkScalar QuadYAtT(const SkPoint a[3], double t) {
158 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
159 double y;
160 xy_at_t(quad, t, *(double*) 0, y);
161 return SkDoubleToScalar(y);
162}
163
164static SkScalar CubicYAtT(const SkPoint a[4], double t) {
165 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
166 {a[3].fX, a[3].fY}};
167 double y;
168 xy_at_t(cubic, t, *(double*) 0, y);
169 return SkDoubleToScalar(y);
170}
171
caryclark@google.com6680fb12012-02-07 22:10:51 +0000172static void LineSubDivide(const SkPoint a[2], double startT, double endT,
173 SkPoint sub[2]) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000174 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com6680fb12012-02-07 22:10:51 +0000175 _Line dst;
176 sub_divide(aLine, startT, endT, dst);
177 sub[0].fX = SkDoubleToScalar(dst[0].x);
178 sub[0].fY = SkDoubleToScalar(dst[0].y);
179 sub[1].fX = SkDoubleToScalar(dst[1].x);
180 sub[1].fY = SkDoubleToScalar(dst[1].y);
181}
182
caryclark@google.coma5764232012-03-28 16:20:21 +0000183static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
184 SkPoint sub[3]) {
185 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
186 Quadratic dst;
187 sub_divide(aQuad, startT, endT, dst);
188 sub[0].fX = SkDoubleToScalar(dst[0].x);
189 sub[0].fY = SkDoubleToScalar(dst[0].y);
190 sub[1].fX = SkDoubleToScalar(dst[1].x);
191 sub[1].fY = SkDoubleToScalar(dst[1].y);
192 sub[2].fX = SkDoubleToScalar(dst[2].x);
193 sub[2].fY = SkDoubleToScalar(dst[2].y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000194}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000195
caryclark@google.coma5764232012-03-28 16:20:21 +0000196static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
197 SkPoint sub[4]) {
198 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
199 {a[3].fX, a[3].fY}};
200 Cubic dst;
201 sub_divide(aCubic, startT, endT, dst);
202 sub[0].fX = SkDoubleToScalar(dst[0].x);
203 sub[0].fY = SkDoubleToScalar(dst[0].y);
204 sub[1].fX = SkDoubleToScalar(dst[1].x);
205 sub[1].fY = SkDoubleToScalar(dst[1].y);
206 sub[2].fX = SkDoubleToScalar(dst[2].x);
207 sub[2].fY = SkDoubleToScalar(dst[2].y);
208 sub[3].fX = SkDoubleToScalar(dst[3].x);
209 sub[3].fY = SkDoubleToScalar(dst[3].y);
210}
caryclark@google.comfb173422012-04-10 18:28:55 +0000211
212static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
213 SkRect& bounds) {
214 SkPoint dst[3];
215 QuadSubDivide(a, startT, endT, dst);
216 bounds.fLeft = bounds.fRight = dst[0].fX;
217 bounds.fTop = bounds.fBottom = dst[0].fY;
218 for (int index = 1; index < 3; ++index) {
219 bounds.growToInclude(dst[index].fX, dst[index].fY);
220 }
221}
222
223static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
224 SkRect& bounds) {
225 SkPoint dst[4];
226 CubicSubDivide(a, startT, endT, dst);
227 bounds.fLeft = bounds.fRight = dst[0].fX;
228 bounds.fTop = bounds.fBottom = dst[0].fY;
229 for (int index = 1; index < 4; ++index) {
230 bounds.growToInclude(dst[index].fX, dst[index].fY);
231 }
232}
233
caryclark@google.comc6825902012-02-03 22:07:47 +0000234/*
235list of edges
236bounds for edge
237sort
238active T
239
240if a contour's bounds is outside of the active area, no need to create edges
241*/
242
243/* given one or more paths,
244 find the bounds of each contour, select the active contours
245 for each active contour, compute a set of edges
246 each edge corresponds to one or more lines and curves
247 leave edges unbroken as long as possible
248 when breaking edges, compute the t at the break but leave the control points alone
249
250 */
251
252void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray) {
253 SkPath::Iter iter(path, false);
254 SkPoint pts[4];
255 SkPath::Verb verb;
256 SkRect bounds;
257 bounds.setEmpty();
258 int count = 0;
259 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
260 switch (verb) {
261 case SkPath::kMove_Verb:
262 if (!bounds.isEmpty()) {
263 *boundsArray.append() = bounds;
264 }
265 bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
266 count = 0;
267 break;
268 case SkPath::kLine_Verb:
269 count = 1;
270 break;
271 case SkPath::kQuad_Verb:
272 count = 2;
273 break;
274 case SkPath::kCubic_Verb:
275 count = 3;
276 break;
277 case SkPath::kClose_Verb:
278 count = 0;
279 break;
280 default:
281 SkDEBUGFAIL("bad verb");
282 return;
283 }
284 for (int i = 1; i <= count; ++i) {
285 bounds.growToInclude(pts[i].fX, pts[i].fY);
286 }
287 }
288}
289
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000290static bool extendLine(const SkPoint line[2], const SkPoint& add) {
291 // FIXME: allow this to extend lines that have slopes that are nearly equal
292 SkScalar dx1 = line[1].fX - line[0].fX;
293 SkScalar dy1 = line[1].fY - line[0].fY;
294 SkScalar dx2 = add.fX - line[0].fX;
295 SkScalar dy2 = add.fY - line[0].fY;
296 return dx1 * dy2 == dx2 * dy1;
297}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000298
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000299// OPTIMIZATION: this should point to a list of input data rather than duplicating
300// the line data here. This would reduce the need to assemble the results.
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000301struct OutEdge {
302 bool operator<(const OutEdge& rh) const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000303 const SkPoint& first = fPts[0];
304 const SkPoint& rhFirst = rh.fPts[0];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000305 return first.fY == rhFirst.fY
306 ? first.fX < rhFirst.fX
307 : first.fY < rhFirst.fY;
308 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000309
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000310 SkPoint fPts[4];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000311 int fID; // id of edge generating data
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000312 uint8_t fVerb; // FIXME: not read from everywhere
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000313 bool fCloseCall; // edge is trimmable if not originally coincident
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000314};
315
caryclark@google.com6680fb12012-02-07 22:10:51 +0000316class OutEdgeBuilder {
317public:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000318 OutEdgeBuilder(bool fill)
319 : fFill(fill) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000320 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000321
caryclark@google.coma5764232012-03-28 16:20:21 +0000322 void addCurve(const SkPoint line[4], SkPath::Verb verb, int id,
323 bool closeCall) {
caryclark@google.comc17972e2012-02-20 21:33:22 +0000324 OutEdge& newEdge = fEdges.push_back();
caryclark@google.coma5764232012-03-28 16:20:21 +0000325 memcpy(newEdge.fPts, line, (verb + 1) * sizeof(SkPoint));
326 newEdge.fVerb = verb;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000327 newEdge.fID = id;
328 newEdge.fCloseCall = closeCall;
329 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000330
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000331 bool trimLine(SkScalar y, int id) {
332 size_t count = fEdges.count();
333 while (count-- != 0) {
334 OutEdge& edge = fEdges[count];
335 if (edge.fID != id) {
336 continue;
337 }
338 if (edge.fCloseCall) {
339 return false;
340 }
341 SkASSERT(edge.fPts[0].fY <= y);
342 if (edge.fPts[1].fY <= y) {
343 continue;
344 }
345 edge.fPts[1].fX = edge.fPts[0].fX + (y - edge.fPts[0].fY)
346 * (edge.fPts[1].fX - edge.fPts[0].fX)
347 / (edge.fPts[1].fY - edge.fPts[0].fY);
348 edge.fPts[1].fY = y;
349 if (gShowDebugf) {
350 SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id,
351 edge.fPts[1].fX, y);
352 }
353 return true;
354 }
355 return false;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000356 }
357
358 void assemble(SkPath& simple) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000359 size_t listCount = fEdges.count();
360 if (listCount == 0) {
361 return;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000362 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000363 do {
364 size_t listIndex = 0;
365 int advance = 1;
366 while (listIndex < listCount && fTops[listIndex] == 0) {
367 ++listIndex;
368 }
369 if (listIndex >= listCount) {
370 break;
371 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000372 int closeEdgeIndex = -listIndex - 1;
caryclark@google.comfb173422012-04-10 18:28:55 +0000373 // the curve is deferred and not added right away because the
374 // following edge may extend the first curve.
caryclark@google.coma5764232012-03-28 16:20:21 +0000375 SkPoint firstPt, lastCurve[4];
376 uint8_t lastVerb;
caryclark@google.comfb173422012-04-10 18:28:55 +0000377#if DEBUG_ASSEMBLE
378 int firstIndex, lastIndex;
379 const int tab = 8;
380#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +0000381 bool doMove = true;
382 int edgeIndex;
383 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000384 SkPoint* ptArray = fEdges[listIndex].fPts;
385 uint8_t verb = fEdges[listIndex].fVerb;
caryclark@google.comfb173422012-04-10 18:28:55 +0000386 SkPoint* curve[4];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000387 if (advance < 0) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000388 curve[0] = &ptArray[verb];
389 if (verb == SkPath::kCubic_Verb) {
390 curve[1] = &ptArray[2];
391 curve[2] = &ptArray[1];
392 }
393 curve[verb] = &ptArray[0];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000394 } else {
caryclark@google.comfb173422012-04-10 18:28:55 +0000395 curve[0] = &ptArray[0];
396 if (verb == SkPath::kCubic_Verb) {
397 curve[1] = &ptArray[1];
398 curve[2] = &ptArray[2];
399 }
400 curve[verb] = &ptArray[verb];
401 }
402 if (verb == SkPath::kQuad_Verb) {
403 curve[1] = &ptArray[1];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000404 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000405 if (doMove) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000406 firstPt = *curve[0];
407 simple.moveTo(curve[0]->fX, curve[0]->fY);
408#if DEBUG_ASSEMBLE
409 SkDebugf("%s %d moveTo (%g,%g)\n", __FUNCTION__,
410 listIndex + 1, curve[0]->fX, curve[0]->fY);
411 firstIndex = listIndex;
412#endif
413 for (int index = 0; index <= verb; ++index) {
414 lastCurve[index] = *curve[index];
caryclark@google.coma5764232012-03-28 16:20:21 +0000415 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000416 doMove = false;
417 } else {
caryclark@google.comfb173422012-04-10 18:28:55 +0000418 bool gap = lastCurve[lastVerb] != *curve[0];
419 if (gap || lastVerb != SkPath::kLine_Verb) { // output the accumulated curve before the gap
caryclark@google.coma5764232012-03-28 16:20:21 +0000420 // FIXME: see comment in bridge -- this probably
421 // conceals errors
caryclark@google.comfb173422012-04-10 18:28:55 +0000422 SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY,
423 curve[0]->fY) <= 10);
caryclark@google.coma5764232012-03-28 16:20:21 +0000424 switch (lastVerb) {
425 case SkPath::kLine_Verb:
426 simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
427 break;
428 case SkPath::kQuad_Verb:
429 simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
430 lastCurve[2].fX, lastCurve[2].fY);
431 break;
432 case SkPath::kCubic_Verb:
433 simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
434 lastCurve[2].fX, lastCurve[2].fY,
435 lastCurve[3].fX, lastCurve[3].fY);
436 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000437 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000438#if DEBUG_ASSEMBLE
439 SkDebugf("%*s %d %sTo (%g,%g)\n", tab, "", lastIndex + 1,
440 kLVerbStr[lastVerb], lastCurve[lastVerb].fX,
441 lastCurve[lastVerb].fY);
442#endif
caryclark@google.coma5764232012-03-28 16:20:21 +0000443 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000444 int firstCopy = 1;
445 if (gap || (lastVerb == SkPath::kLine_Verb &&
446 !extendLine(lastCurve, *curve[verb]))) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000447 // FIXME: see comment in bridge -- this probably
448 // conceals errors
caryclark@google.comfb173422012-04-10 18:28:55 +0000449 SkASSERT(lastCurve[lastVerb] == *curve[0] ||
450 (fFill && UlpsDiff(lastCurve[lastVerb].fY,
451 curve[0]->fY) <= 10));
452 simple.lineTo(curve[0]->fX, curve[0]->fY);
453#if DEBUG_ASSEMBLE
454 SkDebugf("%*s %d gap lineTo (%g,%g)\n", tab, "",
455 lastIndex + 1, curve[0]->fX, curve[0]->fY);
456#endif
457 firstCopy = 0;
458 } else if (lastVerb != SkPath::kLine_Verb) {
459 firstCopy = 0;
caryclark@google.coma5764232012-03-28 16:20:21 +0000460 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000461 for (int index = firstCopy; index <= verb; ++index) {
462 lastCurve[index] = *curve[index];
caryclark@google.coma5764232012-03-28 16:20:21 +0000463 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000464 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000465 lastVerb = verb;
466#if DEBUG_ASSEMBLE
467 lastIndex = listIndex;
468#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +0000469 if (advance < 0) {
470 edgeIndex = fTops[listIndex];
471 fTops[listIndex] = 0;
caryclark@google.comfb173422012-04-10 18:28:55 +0000472 } else {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000473 edgeIndex = fBottoms[listIndex];
474 fBottoms[listIndex] = 0;
475 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000476 if (edgeIndex) {
477 listIndex = abs(edgeIndex) - 1;
478 if (edgeIndex < 0) {
479 fTops[listIndex] = 0;
480 } else {
481 fBottoms[listIndex] = 0;
482 }
483 }
484 if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000485 switch (lastVerb) {
486 case SkPath::kLine_Verb:
487 simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
488 break;
489 case SkPath::kQuad_Verb:
490 simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
491 lastCurve[2].fX, lastCurve[2].fY);
492 break;
493 case SkPath::kCubic_Verb:
494 simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
495 lastCurve[2].fX, lastCurve[2].fY,
496 lastCurve[3].fX, lastCurve[3].fY);
497 break;
498 }
499#if DEBUG_ASSEMBLE
500 SkDebugf("%*s %d %sTo last (%g, %g)\n", tab, "",
501 lastIndex + 1, kLVerbStr[lastVerb],
502 lastCurve[lastVerb].fX, lastCurve[lastVerb].fY);
503#endif
caryclark@google.coma5764232012-03-28 16:20:21 +0000504 if (lastCurve[lastVerb] != firstPt) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000505 simple.lineTo(firstPt.fX, firstPt.fY);
506#if DEBUG_ASSEMBLE
507 SkDebugf("%*s %d final line (%g, %g)\n", tab, "",
508 firstIndex + 1, firstPt.fX, firstPt.fY);
509#endif
caryclark@google.com4917f172012-03-05 22:01:21 +0000510 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000511 simple.close();
caryclark@google.comfb173422012-04-10 18:28:55 +0000512#if DEBUG_ASSEMBLE
513 SkDebugf("%*s close\n", tab, "");
514#endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000515 break;
516 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000517 // if this and next edge go different directions
518#if DEBUG_ASSEMBLE
519 SkDebugf("%*s advance=%d edgeIndex=%d flip=%s\n", tab, "",
520 advance, edgeIndex, advance > 0 ^ edgeIndex < 0 ?
521 "true" : "false");
522#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +0000523 if (advance > 0 ^ edgeIndex < 0) {
524 advance = -advance;
525 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000526 } while (edgeIndex);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000527 } while (true);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000528 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000529
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000530 // sort points by y, then x
531 // if x/y is identical, sort bottoms before tops
532 // if identical and both tops/bottoms, sort by angle
533 static bool lessThan(SkTArray<OutEdge>& edges, const int one,
534 const int two) {
535 const OutEdge& oneEdge = edges[abs(one) - 1];
536 int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
537 const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
538 const OutEdge& twoEdge = edges[abs(two) - 1];
539 int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
540 const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
541 if (startPt1.fY != startPt2.fY) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000542 #if DEBUG_OUT_LESS_THAN
543 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
544 one, two, startPt1.fY, startPt2.fY,
545 startPt1.fY < startPt2.fY ? "true" : "false");
546 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000547 return startPt1.fY < startPt2.fY;
548 }
549 if (startPt1.fX != startPt2.fX) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000550 #if DEBUG_OUT_LESS_THAN
551 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
552 one, two, startPt1.fX, startPt2.fX,
553 startPt1.fX < startPt2.fX ? "true" : "false");
554 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000555 return startPt1.fX < startPt2.fX;
556 }
557 const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
558 const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
559 SkScalar dy1 = startPt1.fY - endPt1.fY;
560 SkScalar dy2 = startPt2.fY - endPt2.fY;
561 SkScalar dy1y2 = dy1 * dy2;
562 if (dy1y2 < 0) { // different signs
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000563 #if DEBUG_OUT_LESS_THAN
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000564 SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
565 dy1 > 0 ? "true" : "false");
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000566 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000567 return dy1 > 0; // one < two if one goes up and two goes down
568 }
569 if (dy1y2 == 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000570 #if DEBUG_OUT_LESS_THAN
571 SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
572 one, two, endPt1.fX < endPt2.fX ? "true" : "false");
573 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000574 return endPt1.fX < endPt2.fX;
575 }
576 SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
577 SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000578 #if DEBUG_OUT_LESS_THAN
579 SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
580 one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
581 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000582 return dy2 > 0 ^ dx1y2 < dx2y1;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000583 }
584
caryclark@google.com6008c6562012-02-15 22:01:16 +0000585 // Sort the indices of paired points and then create more indices so
586 // assemble() can find the next edge and connect the top or bottom
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000587 void bridge() {
588 size_t index;
589 size_t count = fEdges.count();
590 if (!count) {
591 return;
592 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000593 SkASSERT(!fFill || count > 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000594 fTops.setCount(count);
595 sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
596 fBottoms.setCount(count);
597 sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000598 SkTDArray<int> order;
599 for (index = 1; index <= count; ++index) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000600 *order.append() = -index;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000601 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000602 for (index = 1; index <= count; ++index) {
603 *order.append() = index;
604 }
605 QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000606 int* lastPtr = order.end() - 1;
607 int* leftPtr = order.begin();
608 while (leftPtr < lastPtr) {
609 int leftIndex = *leftPtr;
610 int leftOutIndex = abs(leftIndex) - 1;
611 const OutEdge& left = fEdges[leftOutIndex];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000612 int* rightPtr = leftPtr + 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000613 int rightIndex = *rightPtr;
614 int rightOutIndex = abs(rightIndex) - 1;
615 const OutEdge& right = fEdges[rightOutIndex];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000616 bool pairUp = fFill;
617 if (!pairUp) {
618 const SkPoint& leftMatch =
619 left.fPts[leftIndex < 0 ? 0 : left.fVerb];
620 const SkPoint& rightMatch =
621 right.fPts[rightIndex < 0 ? 0 : right.fVerb];
622 pairUp = leftMatch == rightMatch;
623 } else {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000624 #if DEBUG_OUT
caryclark@google.comd88e0892012-03-27 13:23:51 +0000625 // FIXME : not happy that error in low bit is allowed
626 // this probably conceals error elsewhere
627 if (UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
628 right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) > 1) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000629 *fMismatches.append() = leftIndex;
630 if (rightPtr == lastPtr) {
631 *fMismatches.append() = rightIndex;
632 }
633 pairUp = false;
634 }
635 #else
caryclark@google.comd88e0892012-03-27 13:23:51 +0000636 SkASSERT(UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
637 right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) <= 10);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000638 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000639 }
640 if (pairUp) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000641 if (leftIndex < 0) {
642 fTops[leftOutIndex] = rightIndex;
643 } else {
644 fBottoms[leftOutIndex] = rightIndex;
645 }
646 if (rightIndex < 0) {
647 fTops[rightOutIndex] = leftIndex;
648 } else {
649 fBottoms[rightOutIndex] = leftIndex;
650 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000651 ++rightPtr;
652 }
653 leftPtr = rightPtr;
654 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000655#if DEBUG_OUT
656 int* mismatch = fMismatches.begin();
657 while (mismatch != fMismatches.end()) {
658 int leftIndex = *mismatch++;
659 int leftOutIndex = abs(leftIndex) - 1;
660 const OutEdge& left = fEdges[leftOutIndex];
661 const SkPoint& leftPt = left.fPts[leftIndex < 0 ? 0 : left.fVerb];
662 SkDebugf("%s left=%d %s (%1.9g,%1.9g)\n",
663 __FUNCTION__, left.fID, leftIndex < 0 ? "top" : "bot",
664 leftPt.fX, leftPt.fY);
665 }
666 SkASSERT(fMismatches.count() == 0);
667#endif
caryclark@google.comfb173422012-04-10 18:28:55 +0000668#if DEBUG_BRIDGE
669 for (index = 0; index < count; ++index) {
670 const OutEdge& edge = fEdges[index];
671 uint8_t verb = edge.fVerb;
672 SkDebugf("%s %d edge=%d %s (%1.9g,%1.9g) (%1.9g,%1.9g)\n",
673 index == 0 ? __FUNCTION__ : " ",
674 index + 1, edge.fID, kLVerbStr[verb], edge.fPts[0].fX,
675 edge.fPts[0].fY, edge.fPts[verb].fX, edge.fPts[verb].fY);
676 }
677 for (index = 0; index < count; ++index) {
678 SkDebugf(" top of % 2d connects to %s of % 2d\n", index + 1,
679 fTops[index] < 0 ? "top " : "bottom", abs(fTops[index]));
680 SkDebugf(" bottom of % 2d connects to %s of % 2d\n", index + 1,
681 fBottoms[index] < 0 ? "top " : "bottom", abs(fBottoms[index]));
682 }
683#endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000684 }
685
caryclark@google.com6008c6562012-02-15 22:01:16 +0000686protected:
caryclark@google.com6680fb12012-02-07 22:10:51 +0000687 SkTArray<OutEdge> fEdges;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000688 SkTDArray<int> fTops;
689 SkTDArray<int> fBottoms;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000690 bool fFill;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000691#if DEBUG_OUT
692 SkTDArray<int> fMismatches;
693#endif
caryclark@google.com6680fb12012-02-07 22:10:51 +0000694};
695
caryclark@google.comc6825902012-02-03 22:07:47 +0000696// Bounds, unlike Rect, does not consider a vertical line to be empty.
697struct Bounds : public SkRect {
698 static bool Intersects(const Bounds& a, const Bounds& b) {
699 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
700 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
701 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000702
caryclark@google.com6008c6562012-02-15 22:01:16 +0000703 bool isEmpty() {
704 return fLeft > fRight || fTop > fBottom
705 || fLeft == fRight && fTop == fBottom
706 || isnan(fLeft) || isnan(fRight)
707 || isnan(fTop) || isnan(fBottom);
708 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000709};
710
caryclark@google.com4917f172012-03-05 22:01:21 +0000711class Intercepts {
712public:
713 Intercepts()
714 : fTopIntercepts(0)
caryclark@google.com198e0542012-03-30 18:47:02 +0000715 , fBottomIntercepts(0)
716 , fExplicit(false) {
717 }
718
719 Intercepts& operator=(const Intercepts& src) {
720 fTs = src.fTs;
721 fTopIntercepts = src.fTopIntercepts;
722 fBottomIntercepts = src.fBottomIntercepts;
723 }
724
725 // OPTIMIZATION: remove this function if it's never called
726 double t(int tIndex) const {
727 if (tIndex == 0) {
728 return 0;
729 }
730 if (tIndex > fTs.count()) {
731 return 1;
732 }
733 return fTs[tIndex - 1];
caryclark@google.com4917f172012-03-05 22:01:21 +0000734 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000735
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000736#if DEBUG_DUMP
caryclark@google.coma5764232012-03-28 16:20:21 +0000737 void dump(const SkPoint* pts, SkPath::Verb verb) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000738 const char className[] = "Intercepts";
739 const int tab = 8;
740 for (int i = 0; i < fTs.count(); ++i) {
741 SkPoint out;
caryclark@google.coma5764232012-03-28 16:20:21 +0000742 switch (verb) {
743 case SkPath::kLine_Verb:
744 LineXYAtT(pts, fTs[i], &out);
745 break;
746 case SkPath::kQuad_Verb:
747 QuadXYAtT(pts, fTs[i], &out);
748 break;
749 case SkPath::kCubic_Verb:
750 CubicXYAtT(pts, fTs[i], &out);
751 break;
752 default:
753 SkASSERT(0);
754 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000755 SkDebugf("%*s.fTs[%d]=%1.9g (%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000756 className, i, fTs[i], out.fX, out.fY);
757 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000758 SkDebugf("%*s.fTopIntercepts=%u\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000759 className, fTopIntercepts);
caryclark@google.com198e0542012-03-30 18:47:02 +0000760 SkDebugf("%*s.fBottomIntercepts=%u\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000761 className, fBottomIntercepts);
caryclark@google.comfb173422012-04-10 18:28:55 +0000762 SkDebugf("%*s.fExplicit=%d\n", tab + sizeof(className),
763 className, fExplicit);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000764 }
765#endif
766
caryclark@google.comc6825902012-02-03 22:07:47 +0000767 SkTDArray<double> fTs;
caryclark@google.com198e0542012-03-30 18:47:02 +0000768 unsigned char fTopIntercepts; // 0=init state 1=1 edge >1=multiple edges
769 unsigned char fBottomIntercepts;
770 bool fExplicit; // if set, suppress 0 and 1
771
caryclark@google.comc6825902012-02-03 22:07:47 +0000772};
773
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000774struct HorizontalEdge {
775 bool operator<(const HorizontalEdge& rh) const {
776 return fY == rh.fY ? fLeft == rh.fLeft ? fRight < rh.fRight
777 : fLeft < rh.fLeft : fY < rh.fY;
778 }
779
780#if DEBUG_DUMP
781 void dump() {
782 const char className[] = "HorizontalEdge";
783 const int tab = 4;
caryclark@google.com752b60e2012-03-22 21:11:17 +0000784 SkDebugf("%*s.fLeft=%1.9g\n", tab + sizeof(className), className, fLeft);
785 SkDebugf("%*s.fRight=%1.9g\n", tab + sizeof(className), className, fRight);
786 SkDebugf("%*s.fY=%1.9g\n", tab + sizeof(className), className, fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000787 }
788#endif
789
790 SkScalar fLeft;
791 SkScalar fRight;
792 SkScalar fY;
793};
794
caryclark@google.com6680fb12012-02-07 22:10:51 +0000795struct InEdge {
796 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000797 return fBounds.fTop == rh.fBounds.fTop
798 ? fBounds.fLeft < rh.fBounds.fLeft
799 : fBounds.fTop < rh.fBounds.fTop;
800 }
801
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000802 // Avoid collapsing t values that are close to the same since
803 // we walk ts to describe consecutive intersections. Since a pair of ts can
804 // be nearly equal, any problems caused by this should be taken care
805 // of later.
806 int add(double* ts, size_t count, ptrdiff_t verbIndex) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000807 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
caryclark@google.com6008c6562012-02-15 22:01:16 +0000808 bool foundIntercept = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000809 int insertedAt = -1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000810 Intercepts& intercepts = fIntercepts[verbIndex];
caryclark@google.comc6825902012-02-03 22:07:47 +0000811 for (size_t index = 0; index < count; ++index) {
812 double t = ts[index];
caryclark@google.com4917f172012-03-05 22:01:21 +0000813 if (t <= 0) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000814 intercepts.fTopIntercepts <<= 1;
caryclark@google.com4917f172012-03-05 22:01:21 +0000815 fContainsIntercepts |= ++intercepts.fTopIntercepts > 1;
816 continue;
817 }
818 if (t >= 1) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000819 intercepts.fBottomIntercepts <<= 1;
caryclark@google.com4917f172012-03-05 22:01:21 +0000820 fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000821 continue;
822 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000823 fIntersected = true;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000824 foundIntercept = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000825 size_t tCount = intercepts.fTs.count();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000826 double delta;
caryclark@google.comc6825902012-02-03 22:07:47 +0000827 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
828 if (t <= intercepts.fTs[idx2]) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000829 // FIXME: ? if (t < intercepts.fTs[idx2]) // failed
830 delta = intercepts.fTs[idx2] - t;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000831 if (delta > 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000832 insertedAt = idx2;
caryclark@google.comc6825902012-02-03 22:07:47 +0000833 *intercepts.fTs.insert(idx2) = t;
caryclark@google.comc6825902012-02-03 22:07:47 +0000834 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000835 goto nextPt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000836 }
837 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000838 if (tCount == 0 || (delta = t - intercepts.fTs[tCount - 1]) > 0) {
839 insertedAt = tCount;
caryclark@google.comc6825902012-02-03 22:07:47 +0000840 *intercepts.fTs.append() = t;
841 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000842 nextPt:
843 ;
caryclark@google.comc6825902012-02-03 22:07:47 +0000844 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000845 fContainsIntercepts |= foundIntercept;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000846 return insertedAt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000847 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000848
caryclark@google.comfb173422012-04-10 18:28:55 +0000849 void addPartial(SkTArray<InEdge>& edges, int ptStart, int ptEnd,
caryclark@google.com198e0542012-03-30 18:47:02 +0000850 int verbStart, int verbEnd) {
851 InEdge* edge = edges.push_back_n(1);
852 int verbCount = verbEnd - verbStart;
853 edge->fIntercepts.push_back_n(verbCount);
854 uint8_t* verbs = &fVerbs[verbStart];
855 for (int ceptIdx = 0; ceptIdx < verbCount; ++ceptIdx) {
856 edge->fIntercepts[ceptIdx] = fIntercepts[verbStart + ceptIdx];
857 }
858 edge->fPts.append(ptEnd - ptStart, &fPts[ptStart]);
859 edge->fVerbs.append(verbCount, &fVerbs[verbStart]);
860 edge->setBounds();
caryclark@google.com198e0542012-03-30 18:47:02 +0000861 edge->fWinding = fWinding;
862 edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
863 }
864
caryclark@google.comfb173422012-04-10 18:28:55 +0000865 void addSplit(SkTArray<InEdge>& edges, SkPoint* pts, uint8_t verb,
866 Intercepts& intercepts, int firstT, int lastT, bool flipped) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000867 InEdge* edge = edges.push_back_n(1);
868 edge->fIntercepts.push_back_n(1);
caryclark@google.comfb173422012-04-10 18:28:55 +0000869 if (firstT == 0) {
870 *edge->fIntercepts[0].fTs.append() = 0;
871 } else {
872 *edge->fIntercepts[0].fTs.append() = intercepts.fTs[firstT - 1];
873 }
874 bool add1 = lastT == intercepts.fTs.count();
875 edge->fIntercepts[0].fTs.append(lastT - firstT, &intercepts.fTs[firstT]);
876 if (add1) {
877 *edge->fIntercepts[0].fTs.append() = 1;
878 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000879 edge->fIntercepts[0].fExplicit = true;
caryclark@google.comfb173422012-04-10 18:28:55 +0000880 edge->fPts.append(verb + 1, pts);
caryclark@google.com198e0542012-03-30 18:47:02 +0000881 edge->fVerbs.append(1, &verb);
caryclark@google.comfb173422012-04-10 18:28:55 +0000882 // FIXME: bounds could be better for partial Ts
883 edge->setSubBounds();
caryclark@google.com198e0542012-03-30 18:47:02 +0000884 edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
885 if (flipped) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000886 edge->flipTs();
887 edge->fWinding = -fWinding;
888 } else {
889 edge->fWinding = fWinding;
caryclark@google.com198e0542012-03-30 18:47:02 +0000890 }
891 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000892
caryclark@google.com6680fb12012-02-07 22:10:51 +0000893 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000894 // FIXME: in the pathological case where there is a ton of edges, binary search?
895 size_t count = fCached.count();
896 for (size_t index = 0; index < count; ++index) {
897 if (edge == fCached[index]) {
898 return true;
899 }
900 if (edge < fCached[index]) {
901 *fCached.insert(index) = edge;
902 return false;
903 }
904 }
905 *fCached.append() = edge;
906 return false;
907 }
908
caryclark@google.comfb173422012-04-10 18:28:55 +0000909 void complete(signed char winding) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000910 setBounds();
911 fIntercepts.push_back_n(fVerbs.count());
912 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
913 flip();
914 }
915 fContainsIntercepts = fIntersected = false;
caryclark@google.com198e0542012-03-30 18:47:02 +0000916 }
917
caryclark@google.comfb173422012-04-10 18:28:55 +0000918 void flip() {
caryclark@google.com198e0542012-03-30 18:47:02 +0000919 size_t index;
920 size_t last = fPts.count() - 1;
921 for (index = 0; index < last; ++index, --last) {
922 SkTSwap<SkPoint>(fPts[index], fPts[last]);
923 }
924 last = fVerbs.count() - 1;
925 for (index = 0; index < last; ++index, --last) {
926 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
927 }
928 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000929
930 void flipTs() {
931 SkASSERT(fIntercepts.count() == 1);
932 Intercepts& intercepts = fIntercepts[0];
933 SkASSERT(intercepts.fExplicit);
934 SkTDArray<double>& ts = intercepts.fTs;
935 size_t index;
936 size_t last = ts.count() - 1;
937 for (index = 0; index < last; ++index, --last) {
938 SkTSwap<double>(ts[index], ts[last]);
939 }
940 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000941
942 void reset() {
943 fCached.reset();
944 fIntercepts.reset();
945 fPts.reset();
946 fVerbs.reset();
947 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com198e0542012-03-30 18:47:02 +0000948 fWinding = 0;
949 fContainsIntercepts = false;
950 fIntersected = false;
951 }
952
953 void setBounds() {
caryclark@google.comc6825902012-02-03 22:07:47 +0000954 SkPoint* ptPtr = fPts.begin();
955 SkPoint* ptLast = fPts.end();
956 if (ptPtr == ptLast) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000957 SkDebugf("%s empty edge\n", __FUNCTION__);
caryclark@google.comc6825902012-02-03 22:07:47 +0000958 SkASSERT(0);
959 // FIXME: delete empty edge?
960 return;
961 }
962 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
963 ++ptPtr;
964 while (ptPtr != ptLast) {
965 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
966 ++ptPtr;
967 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000968 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000969
970 // recompute bounds based on subrange of T values
971 void setSubBounds() {
972 SkASSERT(fIntercepts.count() == 1);
973 Intercepts& intercepts = fIntercepts[0];
974 SkASSERT(intercepts.fExplicit);
975 SkASSERT(fVerbs.count() == 1);
976 SkTDArray<double>& ts = intercepts.fTs;
977 if (fVerbs[0] == SkPath::kQuad_Verb) {
978 SkASSERT(fPts.count() == 3);
979 QuadSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
980 } else {
981 SkASSERT(fVerbs[0] == SkPath::kCubic_Verb);
982 SkASSERT(fPts.count() == 4);
983 CubicSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
984 }
985 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000986
caryclark@google.comfb173422012-04-10 18:28:55 +0000987 void splitInflectionPts(SkTArray<InEdge>& edges) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000988 if (!fIntersected) {
989 return;
990 }
991 uint8_t* verbs = fVerbs.begin();
992 SkPoint* pts = fPts.begin();
993 int lastVerb = 0;
994 int lastPt = 0;
995 uint8_t verb;
996 bool edgeSplit = false;
997 for (int ceptIdx = 0; ceptIdx < fIntercepts.count(); ++ceptIdx, pts += verb) {
998 Intercepts& intercepts = fIntercepts[ceptIdx];
999 verb = *verbs++;
1000 if (verb <= SkPath::kLine_Verb) {
1001 continue;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001002 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001003 size_t tCount = intercepts.fTs.count();
1004 if (!tCount) {
1005 continue;
1006 }
1007 size_t tIndex = -1;
1008 SkScalar y = pts[0].fY;
1009 int lastSplit = 0;
1010 int firstSplit = -1;
1011 bool curveSplit = false;
1012 while (++tIndex < tCount) {
1013 double nextT = intercepts.fTs[tIndex];
1014 SkScalar nextY = verb == SkPath::kQuad_Verb
1015 ? QuadYAtT(pts, nextT) : CubicYAtT(pts, nextT);
1016 if (nextY < y) {
1017 edgeSplit = curveSplit = true;
1018 if (firstSplit < 0) {
1019 firstSplit = tIndex;
1020 int nextPt = pts - fPts.begin();
1021 int nextVerb = verbs - 1 - fVerbs.begin();
1022 if (lastVerb < nextVerb) {
caryclark@google.comfb173422012-04-10 18:28:55 +00001023 addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
caryclark@google.com198e0542012-03-30 18:47:02 +00001024 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001025 SkDebugf("%s addPartial 1\n", __FUNCTION__);
caryclark@google.com198e0542012-03-30 18:47:02 +00001026 #endif
1027 }
1028 lastPt = nextPt;
1029 lastVerb = nextVerb;
1030 }
1031 } else {
1032 if (firstSplit >= 0) {
1033 if (lastSplit < firstSplit) {
caryclark@google.comfb173422012-04-10 18:28:55 +00001034 addSplit(edges, pts, verb, intercepts,
1035 lastSplit, firstSplit, false);
caryclark@google.com198e0542012-03-30 18:47:02 +00001036 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001037 SkDebugf("%s addSplit 1 tIndex=%d,%d\n",
1038 __FUNCTION__, lastSplit, firstSplit);
caryclark@google.com198e0542012-03-30 18:47:02 +00001039 #endif
1040 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001041 addSplit(edges, pts, verb, intercepts,
1042 firstSplit, tIndex, true);
caryclark@google.com198e0542012-03-30 18:47:02 +00001043 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001044 SkDebugf("%s addSplit 2 tIndex=%d,%d flip\n",
1045 __FUNCTION__, firstSplit, tIndex);
caryclark@google.com198e0542012-03-30 18:47:02 +00001046 #endif
1047 lastSplit = tIndex;
1048 firstSplit = -1;
1049 }
1050 }
1051 y = nextY;
1052 }
1053 if (curveSplit) {
1054 if (firstSplit < 0) {
1055 firstSplit = lastSplit;
1056 } else {
caryclark@google.comfb173422012-04-10 18:28:55 +00001057 addSplit(edges, pts, verb, intercepts, lastSplit,
1058 firstSplit, false);
caryclark@google.com198e0542012-03-30 18:47:02 +00001059 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001060 SkDebugf("%s addSplit 3 tIndex=%d,%d\n", __FUNCTION__,
1061 lastSplit, firstSplit);
caryclark@google.com198e0542012-03-30 18:47:02 +00001062 #endif
1063 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001064 addSplit(edges, pts, verb, intercepts, firstSplit,
1065 tIndex, pts[verb].fY < y);
caryclark@google.com198e0542012-03-30 18:47:02 +00001066 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001067 SkDebugf("%s addSplit 4 tIndex=%d,%d %s\n", __FUNCTION__,
1068 firstSplit, tIndex, pts[verb].fY < y ? "flip" : "");
caryclark@google.com198e0542012-03-30 18:47:02 +00001069 #endif
caryclark@google.com6680fb12012-02-07 22:10:51 +00001070 }
1071 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001072 // collapse remainder -- if there's nothing left, clear it somehow?
1073 if (edgeSplit) {
1074 int nextVerb = verbs - 1 - fVerbs.begin();
1075 if (lastVerb < nextVerb) {
1076 int nextPt = pts - fPts.begin();
caryclark@google.comfb173422012-04-10 18:28:55 +00001077 addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
caryclark@google.com198e0542012-03-30 18:47:02 +00001078 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001079 SkDebugf("%s addPartial 2\n", __FUNCTION__);
caryclark@google.com198e0542012-03-30 18:47:02 +00001080 #endif
1081 }
1082 // OPTIMIZATION: reuse the edge instead of marking it empty
1083 reset();
1084 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001085 }
1086
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001087#if DEBUG_DUMP
1088 void dump() {
1089 int i;
1090 const char className[] = "InEdge";
1091 const int tab = 4;
1092 SkDebugf("InEdge %p (edge=%d)\n", this, fID);
1093 for (i = 0; i < fCached.count(); ++i) {
1094 SkDebugf("%*s.fCached[%d]=0x%08x\n", tab + sizeof(className),
1095 className, i, fCached[i]);
1096 }
1097 uint8_t* verbs = fVerbs.begin();
1098 SkPoint* pts = fPts.begin();
1099 for (i = 0; i < fIntercepts.count(); ++i) {
1100 SkDebugf("%*s.fIntercepts[%d]:\n", tab + sizeof(className),
1101 className, i);
caryclark@google.coma5764232012-03-28 16:20:21 +00001102 fIntercepts[i].dump(pts, (SkPath::Verb) *verbs);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001103 pts += *verbs++;
1104 }
1105 for (i = 0; i < fPts.count(); ++i) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001106 SkDebugf("%*s.fPts[%d]=(%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001107 className, i, fPts[i].fX, fPts[i].fY);
1108 }
1109 for (i = 0; i < fVerbs.count(); ++i) {
1110 SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className),
1111 className, i, fVerbs[i]);
1112 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001113 SkDebugf("%*s.fBounds=(%1.9g, %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001114 className, fBounds.fLeft, fBounds.fTop,
1115 fBounds.fRight, fBounds.fBottom);
1116 SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className,
1117 fWinding);
1118 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
1119 className, fContainsIntercepts);
caryclark@google.com198e0542012-03-30 18:47:02 +00001120 SkDebugf("%*s.fIntersected=%d\n", tab + sizeof(className),
1121 className, fIntersected);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001122 }
1123#endif
1124
caryclark@google.com198e0542012-03-30 18:47:02 +00001125 // FIXME: temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +00001126 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +00001127 SkTArray<Intercepts> fIntercepts; // one per verb
caryclark@google.com4917f172012-03-05 22:01:21 +00001128
caryclark@google.comc6825902012-02-03 22:07:47 +00001129 // persistent data
1130 SkTDArray<SkPoint> fPts;
1131 SkTDArray<uint8_t> fVerbs;
1132 Bounds fBounds;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001133 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +00001134 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001135 bool fContainsIntercepts;
caryclark@google.com198e0542012-03-30 18:47:02 +00001136 bool fIntersected;
caryclark@google.comc6825902012-02-03 22:07:47 +00001137};
1138
caryclark@google.com6680fb12012-02-07 22:10:51 +00001139class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +00001140public:
1141
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001142InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges,
1143 SkTDArray<HorizontalEdge>& horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +00001144 : fPath(path)
1145 , fCurrentEdge(NULL)
1146 , fEdges(edges)
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001147 , fHorizontalEdges(horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +00001148 , fIgnoreHorizontal(ignoreHorizontal)
caryclark@google.com198e0542012-03-30 18:47:02 +00001149 , fContainsCurves(false)
caryclark@google.comc6825902012-02-03 22:07:47 +00001150{
1151 walk();
1152}
1153
caryclark@google.com198e0542012-03-30 18:47:02 +00001154bool containsCurves() const {
1155 return fContainsCurves;
1156}
1157
caryclark@google.comc6825902012-02-03 22:07:47 +00001158protected:
1159
1160void addEdge() {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001161 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +00001162 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
1163 fPtOffset = 1;
1164 *fCurrentEdge->fVerbs.append() = fVerb;
1165}
1166
caryclark@google.com6008c6562012-02-15 22:01:16 +00001167bool complete() {
1168 if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
caryclark@google.comfb173422012-04-10 18:28:55 +00001169 fCurrentEdge->complete(fWinding);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001170 fCurrentEdge = NULL;
1171 return true;
1172 }
1173 return false;
1174}
1175
caryclark@google.comc6825902012-02-03 22:07:47 +00001176int direction(int count) {
1177 fPtCount = count;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001178 if (fIgnoreHorizontal && isHorizontal()) {
caryclark@google.comc6825902012-02-03 22:07:47 +00001179 return 0;
1180 }
1181 int last = count - 1;
1182 return fPts[0].fY == fPts[last].fY
caryclark@google.com6680fb12012-02-07 22:10:51 +00001183 ? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX
1184 ? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +00001185}
1186
1187bool isHorizontal() {
1188 SkScalar y = fPts[0].fY;
1189 for (int i = 1; i < fPtCount; ++i) {
1190 if (fPts[i].fY != y) {
1191 return false;
1192 }
1193 }
1194 return true;
1195}
1196
1197void startEdge() {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001198 if (!fCurrentEdge) {
1199 fCurrentEdge = fEdges.push_back_n(1);
1200 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001201 fWinding = 0;
1202 fPtOffset = 0;
1203}
1204
1205void walk() {
1206 SkPath::Iter iter(fPath, true);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001207 int winding = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +00001208 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
1209 switch (fVerb) {
1210 case SkPath::kMove_Verb:
caryclark@google.comc6825902012-02-03 22:07:47 +00001211 startEdge();
1212 continue;
1213 case SkPath::kLine_Verb:
1214 winding = direction(2);
1215 break;
1216 case SkPath::kQuad_Verb:
1217 winding = direction(3);
caryclark@google.com198e0542012-03-30 18:47:02 +00001218 fContainsCurves = true;
caryclark@google.comc6825902012-02-03 22:07:47 +00001219 break;
1220 case SkPath::kCubic_Verb:
1221 winding = direction(4);
caryclark@google.com198e0542012-03-30 18:47:02 +00001222 fContainsCurves = true;
caryclark@google.comc6825902012-02-03 22:07:47 +00001223 break;
1224 case SkPath::kClose_Verb:
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001225 SkASSERT(fCurrentEdge);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001226 complete();
caryclark@google.comc6825902012-02-03 22:07:47 +00001227 continue;
1228 default:
1229 SkDEBUGFAIL("bad verb");
1230 return;
1231 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001232 if (winding == 0) {
1233 HorizontalEdge* horizontalEdge = fHorizontalEdges.append();
1234 // FIXME: for degenerate quads and cubics, compute x extremes
1235 horizontalEdge->fLeft = fPts[0].fX;
1236 horizontalEdge->fRight = fPts[fVerb].fX;
1237 horizontalEdge->fY = fPts[0].fY;
1238 if (horizontalEdge->fLeft > horizontalEdge->fRight) {
1239 SkTSwap<SkScalar>(horizontalEdge->fLeft, horizontalEdge->fRight);
1240 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001241 if (complete()) {
1242 startEdge();
1243 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001244 continue;
1245 }
1246 if (fWinding + winding == 0) {
1247 // FIXME: if prior verb or this verb is a horizontal line, reverse
1248 // it instead of starting a new edge
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001249 SkASSERT(fCurrentEdge);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001250 if (complete()) {
1251 startEdge();
1252 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001253 }
1254 fWinding = winding;
1255 addEdge();
1256 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001257 if (!complete()) {
1258 if (fCurrentEdge) {
1259 fEdges.pop_back();
1260 }
1261 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001262}
1263
1264private:
1265 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001266 InEdge* fCurrentEdge;
1267 SkTArray<InEdge>& fEdges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001268 SkTDArray<HorizontalEdge>& fHorizontalEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +00001269 SkPoint fPts[4];
1270 SkPath::Verb fVerb;
1271 int fPtCount;
1272 int fPtOffset;
1273 int8_t fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +00001274 bool fIgnoreHorizontal;
caryclark@google.com198e0542012-03-30 18:47:02 +00001275 bool fContainsCurves;
caryclark@google.comc6825902012-02-03 22:07:47 +00001276};
1277
caryclark@google.com6680fb12012-02-07 22:10:51 +00001278struct WorkEdge {
1279 SkScalar bottom() const {
1280 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +00001281 }
1282
caryclark@google.com6680fb12012-02-07 22:10:51 +00001283 void init(const InEdge* edge) {
1284 fEdge = edge;
1285 fPts = edge->fPts.begin();
1286 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00001287 }
1288
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001289 bool advance() {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001290 SkASSERT(fVerb < fEdge->fVerbs.end());
1291 fPts += *fVerb++;
1292 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +00001293 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001294
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001295 SkPath::Verb lastVerb() const {
1296 SkASSERT(fVerb > fEdge->fVerbs.begin());
1297 return (SkPath::Verb) fVerb[-1];
1298 }
1299
caryclark@google.comc6825902012-02-03 22:07:47 +00001300
1301 SkPath::Verb verb() const {
1302 return (SkPath::Verb) *fVerb;
1303 }
1304
caryclark@google.com6008c6562012-02-15 22:01:16 +00001305 ptrdiff_t verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001306 return fVerb - fEdge->fVerbs.begin();
1307 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001308
caryclark@google.com6680fb12012-02-07 22:10:51 +00001309 int winding() const {
1310 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +00001311 }
1312
caryclark@google.com6680fb12012-02-07 22:10:51 +00001313 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +00001314 const SkPoint* fPts;
1315 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +00001316};
1317
caryclark@google.com6680fb12012-02-07 22:10:51 +00001318// always constructed with SkTDArray because new edges are inserted
1319// this may be a inappropriate optimization, suggesting that a separate array of
1320// ActiveEdge* may be faster to insert and search
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001321
1322// OPTIMIZATION: Brian suggests that global sorting should be unnecessary, since
1323// as active edges are introduced, only local sorting should be required
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001324class ActiveEdge {
1325public:
caryclark@google.com6008c6562012-02-15 22:01:16 +00001326 bool operator<(const ActiveEdge& rh) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001327 double topD = fAbove.fX - rh.fAbove.fX;
1328 if (rh.fAbove.fY < fAbove.fY) {
1329 topD = (rh.fBelow.fY - rh.fAbove.fY) * topD
1330 - (fAbove.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
1331 } else if (rh.fAbove.fY > fAbove.fY) {
1332 topD = (fBelow.fY - fAbove.fY) * topD
1333 + (rh.fAbove.fY - fAbove.fY) * (fBelow.fX - fAbove.fX);
1334 }
1335 double botD = fBelow.fX - rh.fBelow.fX;
1336 if (rh.fBelow.fY > fBelow.fY) {
1337 botD = (rh.fBelow.fY - rh.fAbove.fY) * botD
1338 - (fBelow.fY - rh.fBelow.fY) * (rh.fBelow.fX - rh.fAbove.fX);
1339 } else if (rh.fBelow.fY < fBelow.fY) {
1340 botD = (fBelow.fY - fAbove.fY) * botD
1341 + (rh.fBelow.fY - fBelow.fY) * (fBelow.fX - fAbove.fX);
1342 }
1343 // return sign of greater absolute value
1344 return (fabs(topD) > fabs(botD) ? topD : botD) < 0;
1345 }
1346
1347 // OPTIMIZATION: fold return statements into one
1348 bool operator_less_than(const ActiveEdge& rh) const {
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001349 if (rh.fAbove.fY - fAbove.fY > fBelow.fY - rh.fAbove.fY
1350 && fBelow.fY < rh.fBelow.fY
1351 || fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - fAbove.fY
1352 && rh.fBelow.fY < fBelow.fY) {
1353 // FIXME: need to compute distance, not check for points equal
1354 const SkPoint& check = rh.fBelow.fY <= fBelow.fY
1355 && fBelow != rh.fBelow ? rh.fBelow :
1356 rh.fAbove;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001357 #if DEBUG_ACTIVE_LESS_THAN
1358 SkDebugf("%s 1 %c %cthis (edge=%d) {%g,%g %g,%g}"
1359 " < rh (edge=%d) {%g,%g %g,%g} upls=(%d)\n", __FUNCTION__,
1360 rh.fBelow.fY <= fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
1361 (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
1362 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX) ? ' '
1363 : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
1364 rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
1365 UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
1366 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX)));
1367 #endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001368 return (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
1369 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX);
1370 }
1371 // FIXME: need to compute distance, not check for points equal
1372 const SkPoint& check = fBelow.fY <= rh.fBelow.fY
1373 && fBelow != rh.fBelow ? fBelow : fAbove;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001374 #if DEBUG_ACTIVE_LESS_THAN
1375 SkDebugf("%s 2 %c %cthis (edge=%d) {%g,%g %g,%g}"
1376 " < rh (edge=%d) {%g,%g %g,%g} upls=(%d) (%d,%d)\n", __FUNCTION__,
1377 fBelow.fY <= rh.fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
1378 (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
1379 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)
1380 ? ' ' : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
1381 rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
1382 UlpsDiff((rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX),
1383 (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)),
1384 UlpsDiff(fBelow.fX, rh.fBelow.fX), UlpsDiff(fBelow.fY, rh.fBelow.fY));
1385 #endif
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001386 return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
1387 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001388 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001389
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001390 // If a pair of edges are nearly coincident for some span, add a T in the
1391 // edge so it can be shortened to match the other edge. Note that another
1392 // approach is to trim the edge after it is added to the OutBuilder list --
1393 // FIXME: since this has no effect if the edge is already done (i.e.,
1394 // fYBottom >= y) maybe this can only be done by calling trimLine later.
1395 void addTatYBelow(SkScalar y) {
1396 if (fBelow.fY <= y || fYBottom >= y) {
1397 return;
1398 }
1399 addTatYInner(y);
1400 fFixBelow = true;
1401 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001402
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001403 void addTatYAbove(SkScalar y) {
1404 if (fBelow.fY <= y) {
1405 return;
1406 }
1407 addTatYInner(y);
1408 }
1409
1410 void addTatYInner(SkScalar y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001411 if (fWorkEdge.fPts[0].fY > y) {
1412 backup(y);
1413 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001414 SkScalar left = fWorkEdge.fPts[0].fX;
1415 SkScalar right = fWorkEdge.fPts[1].fX;
1416 if (left > right) {
1417 SkTSwap(left, right);
1418 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001419 double ts[2];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001420 int pts = LineIntersect(fWorkEdge.fPts, left, right, y, ts);
1421 SkASSERT(pts == 1);
1422 // An ActiveEdge or WorkEdge has no need to modify the T values computed
1423 // in the InEdge, except in the following case. If a pair of edges are
1424 // nearly coincident, this may not be detected when the edges are
1425 // intersected. Later, when sorted, and this near-coincidence is found,
1426 // an additional t value must be added, requiring the cast below.
1427 InEdge* writable = const_cast<InEdge*>(fWorkEdge.fEdge);
1428 int insertedAt = writable->add(ts, pts, fWorkEdge.verbIndex());
caryclark@google.com752b60e2012-03-22 21:11:17 +00001429 #if DEBUG_ADJUST_COINCIDENT
1430 SkDebugf("%s edge=%d y=%1.9g t=%1.9g\n", __FUNCTION__, ID(), y, ts[0]);
1431 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001432 if (insertedAt >= 0) {
1433 if (insertedAt + 1 < fTIndex) {
1434 SkASSERT(insertedAt + 2 == fTIndex);
1435 --fTIndex;
1436 }
1437 }
1438 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001439
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001440 bool advanceT() {
caryclark@google.com198e0542012-03-30 18:47:02 +00001441 SkASSERT(fTIndex <= fTs->count() - fExplicitTs);
1442 return ++fTIndex <= fTs->count() - fExplicitTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001443 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001444
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001445 bool advance() {
1446 // FIXME: flip sense of next
1447 bool result = fWorkEdge.advance();
1448 fDone = !result;
1449 initT();
1450 return result;
1451 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001452
1453 void backup(SkScalar y) {
1454 do {
1455 SkASSERT(fWorkEdge.fEdge->fVerbs.begin() < fWorkEdge.fVerb);
1456 fWorkEdge.fPts -= *--fWorkEdge.fVerb;
1457 SkASSERT(fWorkEdge.fEdge->fPts.begin() <= fWorkEdge.fPts);
1458 } while (fWorkEdge.fPts[0].fY >= y);
1459 initT();
caryclark@google.com198e0542012-03-30 18:47:02 +00001460 SkASSERT(!fExplicitTs);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001461 fTIndex = fTs->count() + 1;
1462 }
1463
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001464 void calcLeft(SkScalar y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001465 // OPTIMIZE: put a kDone_Verb at the end of the verb list?
caryclark@google.com4917f172012-03-05 22:01:21 +00001466 if (fDone || fBelow.fY > y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001467 return; // nothing to do; use last
caryclark@google.com4917f172012-03-05 22:01:21 +00001468 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001469 calcLeft();
caryclark@google.com752b60e2012-03-22 21:11:17 +00001470 if (fAbove.fY == fBelow.fY) {
1471 SkDebugf("%s edge=%d fAbove.fY != fBelow.fY %1.9g\n", __FUNCTION__,
1472 ID(), fAbove.fY);
1473 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001474 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001475
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001476 void calcLeft() {
caryclark@google.coma5764232012-03-28 16:20:21 +00001477 void (*xyAtTFunc)(const SkPoint a[], double t, SkPoint* out);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001478 switch (fWorkEdge.verb()) {
caryclark@google.coma5764232012-03-28 16:20:21 +00001479 case SkPath::kLine_Verb:
1480 xyAtTFunc = LineXYAtT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001481 break;
caryclark@google.coma5764232012-03-28 16:20:21 +00001482 case SkPath::kQuad_Verb:
1483 xyAtTFunc = QuadXYAtT;
1484 break;
1485 case SkPath::kCubic_Verb:
1486 xyAtTFunc = CubicXYAtT;
1487 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001488 default:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001489 SkASSERT(0);
1490 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001491 // OPTIMIZATION: if fXAbove, fXBelow have already been computed
1492 // for the fTIndex, don't do it again
1493 // For identical x, this lets us know which edge is first.
1494 // If both edges have T values < 1, check x at next T (fXBelow).
caryclark@google.com198e0542012-03-30 18:47:02 +00001495 int add = (fTIndex <= fTs->count() - fExplicitTs) - 1;
caryclark@google.coma5764232012-03-28 16:20:21 +00001496 double tAbove = t(fTIndex + add);
1497 (*xyAtTFunc)(fWorkEdge.fPts, tAbove, &fAbove);
1498 double tBelow = t(fTIndex - ~add);
1499 (*xyAtTFunc)(fWorkEdge.fPts, tBelow, &fBelow);
1500 SkASSERT(tAbove != tBelow);
1501 // FIXME: this can loop forever
1502 // need a break if we hit the end
caryclark@google.com198e0542012-03-30 18:47:02 +00001503 // FIXME: in unit test, figure out how explicit Ts work as well
caryclark@google.coma5764232012-03-28 16:20:21 +00001504 while (fAbove.fY == fBelow.fY) {
1505 if (add < 0 || fTIndex == fTs->count()) {
1506 add -= 1;
1507 SkASSERT(fTIndex + add >= 0);
1508 tAbove = t(fTIndex + add);
1509 (*xyAtTFunc)(fWorkEdge.fPts, tAbove, &fAbove);
1510 } else {
1511 add += 1;
1512 SkASSERT(fTIndex - ~add <= fTs->count() + 1);
1513 tBelow = t(fTIndex - ~add);
1514 (*xyAtTFunc)(fWorkEdge.fPts, tBelow, &fBelow);
1515 }
1516 }
1517 #if DEBUG_ABOVE_BELOW
1518 fTAbove = tAbove;
1519 fTBelow = tBelow;
1520 #endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00001521 }
1522
caryclark@google.com752b60e2012-03-22 21:11:17 +00001523 bool done(SkScalar bottom) const {
1524 return fDone || fYBottom >= bottom;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001525 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001526
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001527 void fixBelow() {
1528 if (fFixBelow) {
caryclark@google.coma5764232012-03-28 16:20:21 +00001529 switch (fWorkEdge.verb()) {
1530 case SkPath::kLine_Verb:
1531 LineXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1532 break;
1533 case SkPath::kQuad_Verb:
1534 QuadXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1535 break;
1536 case SkPath::kCubic_Verb:
1537 CubicXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1538 break;
1539 default:
1540 SkASSERT(0);
1541 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001542 fFixBelow = false;
1543 }
1544 }
1545
caryclark@google.com6680fb12012-02-07 22:10:51 +00001546 void init(const InEdge* edge) {
1547 fWorkEdge.init(edge);
1548 initT();
caryclark@google.com4917f172012-03-05 22:01:21 +00001549 fBelow.fY = SK_ScalarMin;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001550 fDone = false;
1551 fYBottom = SK_ScalarMin;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001552 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001553
caryclark@google.com6680fb12012-02-07 22:10:51 +00001554 void initT() {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001555 const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
1556 SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
1557 const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
caryclark@google.com198e0542012-03-30 18:47:02 +00001558 fTs = &interceptPtr->fTs;
1559 fExplicitTs = interceptPtr->fExplicit;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001560 // the above is conceptually the same as
1561 // fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
1562 // but templated arrays don't allow returning a pointer to the end() element
caryclark@google.com6680fb12012-02-07 22:10:51 +00001563 fTIndex = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +00001564 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001565
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001566 // OPTIMIZATION: record if two edges are coincident when the are intersected
1567 // It's unclear how to do this -- seems more complicated than recording the
1568 // t values, since the same t values could exist intersecting non-coincident
1569 // edges.
1570 bool isCoincidentWith(const ActiveEdge* edge, SkScalar y) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001571 if (fAbove != edge->fAbove || fBelow != edge->fBelow) {
1572 return false;
1573 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001574 uint8_t verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
1575 uint8_t edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb()
1576 : edge->fWorkEdge.verb();
1577 if (verb != edgeVerb) {
1578 return false;
1579 }
1580 switch (verb) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001581 case SkPath::kLine_Verb: {
caryclark@google.com4917f172012-03-05 22:01:21 +00001582 return true;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001583 }
1584 default:
1585 // FIXME: add support for all curve types
1586 SkASSERT(0);
1587 }
1588 return false;
1589 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001590
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001591 // The shortest close call edge should be moved into a position where
1592 // it contributes if the winding is transitioning to or from zero.
1593 bool swapClose(const ActiveEdge* next, int prev, int wind, int mask) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001594#if DEBUG_ADJUST_COINCIDENT
1595 SkDebugf("%s edge=%d (%g) next=%d (%g) prev=%d wind=%d nextWind=%d\n",
1596 __FUNCTION__, ID(), fBelow.fY, next->ID(), next->fBelow.fY,
1597 prev, wind, wind + next->fWorkEdge.winding());
1598#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001599 if ((prev & mask) == 0 || (wind & mask) == 0) {
1600 return next->fBelow.fY < fBelow.fY;
1601 }
1602 int nextWinding = wind + next->fWorkEdge.winding();
1603 if ((nextWinding & mask) == 0) {
1604 return fBelow.fY < next->fBelow.fY;
1605 }
1606 return false;
1607 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001608
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001609 bool swapCoincident(const ActiveEdge* edge, SkScalar bottom) const {
1610 if (fBelow.fY >= bottom || fDone || edge->fDone) {
1611 return false;
1612 }
1613 ActiveEdge thisWork = *this;
1614 ActiveEdge edgeWork = *edge;
1615 while ((thisWork.advanceT() || thisWork.advance())
1616 && (edgeWork.advanceT() || edgeWork.advance())) {
1617 thisWork.calcLeft();
1618 edgeWork.calcLeft();
1619 if (thisWork < edgeWork) {
1620 return false;
1621 }
1622 if (edgeWork < thisWork) {
1623 return true;
1624 }
1625 }
1626 return false;
1627 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001628
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001629 bool tooCloseToCall(const ActiveEdge* edge) const {
1630 int ulps;
caryclark@google.comd88e0892012-03-27 13:23:51 +00001631 // FIXME: the first variation works better (or at least causes fewer tests
1632 // to fail than the second, although the second's logic better matches the
1633 // current sort criteria. Need to track down the cause of the crash, and
1634 // see if the second case isn't somehow buggy.
1635#if 01
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001636 // FIXME: don't compare points for equality
1637 // OPTIMIZATION: refactor to make one call to UlpsDiff
1638 if (edge->fAbove.fY - fAbove.fY > fBelow.fY - edge->fAbove.fY
1639 && fBelow.fY < edge->fBelow.fY
1640 || fAbove.fY - edge->fAbove.fY < edge->fBelow.fY - fAbove.fY
1641 && edge->fBelow.fY < fBelow.fY) {
1642 const SkPoint& check = edge->fBelow.fY <= fBelow.fY
1643 && fBelow != edge->fBelow ? edge->fBelow :
1644 edge->fAbove;
1645 ulps = UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
1646 (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX));
1647 } else {
1648 const SkPoint& check = fBelow.fY <= edge->fBelow.fY
1649 && fBelow != edge->fBelow ? fBelow : fAbove;
1650 ulps = UlpsDiff((edge->fBelow.fY - edge->fAbove.fY)
1651 * (check.fX - edge->fAbove.fX),
1652 (check.fY - edge->fAbove.fY)
1653 * (edge->fBelow.fX - edge->fAbove.fX));
1654 }
caryclark@google.comd88e0892012-03-27 13:23:51 +00001655#else
1656 double t1, t2, b1, b2;
1657 if (edge->fAbove.fY < fAbove.fY) {
1658 t1 = (edge->fBelow.fY - edge->fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
1659 t2 = (fAbove.fY - edge->fAbove.fY) * (edge->fBelow.fX - edge->fAbove.fX);
1660 } else if (edge->fAbove.fY > fAbove.fY) {
1661 t1 = (fBelow.fY - fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
1662 t2 = (fAbove.fY - edge->fAbove.fY) * (fBelow.fX - fAbove.fX);
1663 } else {
1664 t1 = fAbove.fX;
1665 t2 = edge->fAbove.fX;
1666 }
1667 if (edge->fBelow.fY > fBelow.fY) {
1668 b1 = (edge->fBelow.fY - edge->fAbove.fY) * (fBelow.fX - edge->fBelow.fX);
1669 b2 = (fBelow.fY - edge->fBelow.fY) * (edge->fBelow.fX - edge->fAbove.fX);
1670 } else if (edge->fBelow.fY < fBelow.fY) {
1671 b1 = (fBelow.fY - fAbove.fY) * (fBelow.fX - edge->fBelow.fX);
1672 b2 = (fBelow.fY - edge->fBelow.fY) * (fBelow.fX - fAbove.fX);
1673 } else {
1674 b1 = fBelow.fX;
1675 b2 = edge->fBelow.fX;
1676 }
1677 if (fabs(t1 - t2) > fabs(b1 - b2)) {
1678 ulps = UlpsDiff(t1, t2);
1679 } else {
1680 ulps = UlpsDiff(b1, b2);
1681 }
1682#if DEBUG_ADJUST_COINCIDENT
1683 SkDebugf("%s this=%d edge=%d ulps=%d\n", __FUNCTION__, ID(), edge->ID(),
1684 ulps);
1685#endif
1686#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001687 return ulps >= 0 && ulps <= 32;
1688 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001689
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001690 double nextT() const {
caryclark@google.com198e0542012-03-30 18:47:02 +00001691 SkASSERT(fTIndex <= fTs->count() - fExplicitTs);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001692 return t(fTIndex + 1);
1693 }
caryclark@google.com6680fb12012-02-07 22:10:51 +00001694
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001695 double t() const {
caryclark@google.com198e0542012-03-30 18:47:02 +00001696 return t(fTIndex);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001697 }
1698
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001699 double t(int tIndex) const {
caryclark@google.com198e0542012-03-30 18:47:02 +00001700 if (fExplicitTs) {
1701 SkASSERT(tIndex < fTs->count());
1702 return (*fTs)[tIndex];
1703 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001704 if (tIndex == 0) {
1705 return 0;
1706 }
1707 if (tIndex > fTs->count()) {
1708 return 1;
1709 }
1710 return (*fTs)[tIndex - 1];
1711 }
1712
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001713 // FIXME: debugging only
caryclark@google.com752b60e2012-03-22 21:11:17 +00001714 int ID() const {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001715 return fWorkEdge.fEdge->fID;
1716 }
1717
1718public:
caryclark@google.com6680fb12012-02-07 22:10:51 +00001719 WorkEdge fWorkEdge;
1720 const SkTDArray<double>* fTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001721 SkPoint fAbove;
1722 SkPoint fBelow;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001723#if DEBUG_ABOVE_BELOW
1724 double fTAbove;
1725 double fTBelow;
1726#endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001727 SkScalar fYBottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001728 int fCoincident;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001729 int fTIndex;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001730 bool fSkip; // OPTIMIZATION: use bitfields?
1731 bool fCloseCall;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001732 bool fDone;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001733 bool fFixBelow;
caryclark@google.com198e0542012-03-30 18:47:02 +00001734 bool fExplicitTs;
caryclark@google.comc6825902012-02-03 22:07:47 +00001735};
1736
caryclark@google.com6680fb12012-02-07 22:10:51 +00001737static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001738 size_t count = activeEdges.count();
1739 for (size_t index = 0; index < count; ++index) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001740 if (edge == activeEdges[index].fWorkEdge.fEdge) {
1741 return;
1742 }
1743 }
1744 ActiveEdge* active = activeEdges.append();
1745 active->init(edge);
1746}
1747
caryclark@google.com4917f172012-03-05 22:01:21 +00001748// Find any intersections in the range of active edges. A pair of edges, on
1749// either side of another edge, may change the winding contribution for part of
1750// the edge.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001751// Keep horizontal edges just for
caryclark@google.com4917f172012-03-05 22:01:21 +00001752// the purpose of computing when edges change their winding contribution, since
1753// this is essentially computing the horizontal intersection.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001754static void addBottomT(InEdge** currentPtr, InEdge** lastPtr,
1755 HorizontalEdge** horizontal) {
1756 InEdge** testPtr = currentPtr - 1;
1757 HorizontalEdge* horzEdge = *horizontal;
1758 SkScalar left = horzEdge->fLeft;
1759 SkScalar bottom = horzEdge->fY;
1760 while (++testPtr != lastPtr) {
1761 InEdge* test = *testPtr;
1762 if (test->fBounds.fBottom <= bottom || test->fBounds.fRight <= left) {
1763 continue;
1764 }
1765 WorkEdge wt;
1766 wt.init(test);
1767 do {
1768 HorizontalEdge** sorted = horizontal;
1769 horzEdge = *sorted;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001770 do {
caryclark@google.com198e0542012-03-30 18:47:02 +00001771 double wtTs[4];
1772 int pts;
1773 uint8_t verb = wt.verb();
1774 switch (verb) {
1775 case SkPath::kLine_Verb:
1776 pts = LineIntersect(wt.fPts, horzEdge->fLeft,
1777 horzEdge->fRight, horzEdge->fY, wtTs);
1778 break;
1779 case SkPath::kQuad_Verb:
1780 pts = QuadIntersect(wt.fPts, horzEdge->fLeft,
1781 horzEdge->fRight, horzEdge->fY, wtTs);
1782 break;
1783 case SkPath::kCubic_Verb:
1784 pts = CubicIntersect(wt.fPts, horzEdge->fLeft,
1785 horzEdge->fRight, horzEdge->fY, wtTs);
1786 break;
1787 }
1788 if (pts) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001789#if DEBUG_ADD_BOTTOM_TS
caryclark@google.com198e0542012-03-30 18:47:02 +00001790 for (int x = 0; x < pts; ++x) {
1791 SkDebugf("%s y=%g wtTs[0]=%g (%g,%g", __FUNCTION__,
1792 horzEdge->fY, wtTs[x], wt.fPts[0].fX, wt.fPts[0].fY);
1793 for (int y = 0; y < verb; ++y) {
1794 SkDebugf(" %g,%g", wt.fPts[y + 1].fX, wt.fPts[y + 1].fY));
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001795 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001796 SkDebugf(")\n");
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001797 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001798 if (pts > verb) {
1799 SkASSERT(0); // FIXME ? should this work?
1800 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1801 }
1802#endif
1803 test->add(wtTs, pts, wt.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001804 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001805 horzEdge = *++sorted;
1806 } while (horzEdge->fY == bottom
1807 && horzEdge->fLeft <= test->fBounds.fRight);
1808 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001809 }
1810}
1811
caryclark@google.coma5764232012-03-28 16:20:21 +00001812static void debugShowLineIntersection(int pts, const WorkEdge& wt,
1813 const WorkEdge& wn, const double wtTs[2], const double wnTs[2]) {
1814#if DEBUG_ADD_INTERSECTING_TS
1815 if (!pts) {
1816 return;
1817 }
1818 SkPoint wtOutPt, wnOutPt;
1819 LineXYAtT(wt.fPts, wtTs[0], &wtOutPt);
1820 LineXYAtT(wn.fPts, wnTs[0], &wnOutPt);
caryclark@google.comfb173422012-04-10 18:28:55 +00001821 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
caryclark@google.coma5764232012-03-28 16:20:21 +00001822 __FUNCTION__,
1823 wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
caryclark@google.comfb173422012-04-10 18:28:55 +00001824 wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY);
caryclark@google.coma5764232012-03-28 16:20:21 +00001825 if (pts == 2) {
1826 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1827 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001828 SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
caryclark@google.coma5764232012-03-28 16:20:21 +00001829 __FUNCTION__,
1830 wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY,
caryclark@google.comfb173422012-04-10 18:28:55 +00001831 wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY);
caryclark@google.coma5764232012-03-28 16:20:21 +00001832 if (pts == 2) {
1833 SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
1834 }
1835#endif
1836}
1837
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001838static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001839 InEdge** testPtr = currentPtr - 1;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001840 // FIXME: lastPtr should be past the point of interest, so
1841 // test below should be lastPtr - 2
1842 // that breaks testSimplifyTriangle22, so further investigation is needed
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001843 while (++testPtr != lastPtr - 1) {
1844 InEdge* test = *testPtr;
1845 InEdge** nextPtr = testPtr;
1846 do {
1847 InEdge* next = *++nextPtr;
caryclark@google.com198e0542012-03-30 18:47:02 +00001848 // FIXME: this compares against the sentinel sometimes
1849 // OPTIMIZATION: this may never be needed since this gets called
1850 // in two passes now. Verify that double hits are appropriate.
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001851 if (test->cached(next)) {
1852 continue;
1853 }
1854 if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
1855 continue;
1856 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001857 WorkEdge wt, wn;
1858 wt.init(test);
1859 wn.init(next);
1860 do {
caryclark@google.coma5764232012-03-28 16:20:21 +00001861 int pts;
1862 Intersections ts;
1863 bool swap = false;
1864 switch (wt.verb()) {
1865 case SkPath::kLine_Verb:
1866 switch (wn.verb()) {
1867 case SkPath::kLine_Verb: {
1868 pts = LineIntersect(wt.fPts, wn.fPts, ts);
1869 debugShowLineIntersection(pts, wt, wn,
1870 ts.fT[0], ts.fT[1]);
1871 break;
1872 }
1873 case SkPath::kQuad_Verb: {
1874 swap = true;
1875 pts = QuadLineIntersect(wn.fPts, wt.fPts, ts);
1876 break;
1877 }
1878 case SkPath::kCubic_Verb: {
1879 swap = true;
1880 pts = CubicLineIntersect(wn.fPts, wt.fPts, ts);
1881 break;
1882 }
1883 default:
1884 SkASSERT(0);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001885 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001886 break;
1887 case SkPath::kQuad_Verb:
1888 switch (wn.verb()) {
1889 case SkPath::kLine_Verb: {
1890 pts = QuadLineIntersect(wt.fPts, wn.fPts, ts);
1891 break;
1892 }
1893 case SkPath::kQuad_Verb: {
1894 pts = QuadIntersect(wt.fPts, wn.fPts, ts);
1895 break;
1896 }
1897 case SkPath::kCubic_Verb: {
1898 // FIXME: promote quad to cubic
1899 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
1900 break;
1901 }
1902 default:
1903 SkASSERT(0);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001904 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001905 break;
1906 case SkPath::kCubic_Verb:
1907 switch (wn.verb()) {
1908 case SkPath::kLine_Verb: {
1909 pts = CubicLineIntersect(wt.fPts, wn.fPts, ts);
1910 break;
1911 }
1912 case SkPath::kQuad_Verb: {
1913 // FIXME: promote quad to cubic
1914 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
1915 break;
1916 }
1917 case SkPath::kCubic_Verb: {
1918 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
1919 break;
1920 }
1921 default:
1922 SkASSERT(0);
1923 }
1924 break;
1925 default:
1926 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001927 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001928 test->add(ts.fT[swap], pts, wt.verbIndex());
1929 next->add(ts.fT[!swap], pts, wn.verbIndex());
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001930 } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
1931 } while (nextPtr != lastPtr);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001932 }
1933}
1934
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001935static InEdge** advanceEdges(SkTDArray<ActiveEdge>* activeEdges,
caryclark@google.com6008c6562012-02-15 22:01:16 +00001936 InEdge** currentPtr, InEdge** lastPtr, SkScalar y) {
1937 InEdge** testPtr = currentPtr - 1;
1938 while (++testPtr != lastPtr) {
1939 if ((*testPtr)->fBounds.fBottom > y) {
1940 continue;
1941 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001942 if (activeEdges) {
1943 InEdge* test = *testPtr;
1944 ActiveEdge* activePtr = activeEdges->begin() - 1;
1945 ActiveEdge* lastActive = activeEdges->end();
1946 while (++activePtr != lastActive) {
1947 if (activePtr->fWorkEdge.fEdge == test) {
1948 activeEdges->remove(activePtr - activeEdges->begin());
1949 break;
1950 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001951 }
1952 }
1953 if (testPtr == currentPtr) {
1954 ++currentPtr;
1955 }
1956 }
1957 return currentPtr;
1958}
1959
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001960// OPTIMIZE: inline?
1961static HorizontalEdge** advanceHorizontal(HorizontalEdge** edge, SkScalar y) {
1962 while ((*edge)->fY < y) {
1963 ++edge;
1964 }
1965 return edge;
1966}
1967
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001968// compute bottom taking into account any intersected edges
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001969static SkScalar computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
1970 SkScalar y, SkScalar bottom) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001971 ActiveEdge* activePtr = activeEdges.begin() - 1;
1972 ActiveEdge* lastActive = activeEdges.end();
1973 while (++activePtr != lastActive) {
1974 const InEdge* test = activePtr->fWorkEdge.fEdge;
1975 if (!test->fContainsIntercepts) {
1976 continue;
1977 }
1978 WorkEdge wt;
1979 wt.init(test);
1980 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001981 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
caryclark@google.com4917f172012-03-05 22:01:21 +00001982 if (intercepts.fTopIntercepts > 1) {
1983 SkScalar yTop = wt.fPts[0].fY;
1984 if (yTop > y && bottom > yTop) {
1985 bottom = yTop;
1986 }
1987 }
1988 if (intercepts.fBottomIntercepts > 1) {
1989 SkScalar yBottom = wt.fPts[wt.verb()].fY;
1990 if (yBottom > y && bottom > yBottom) {
1991 bottom = yBottom;
1992 }
1993 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001994 const SkTDArray<double>& fTs = intercepts.fTs;
1995 size_t count = fTs.count();
1996 for (size_t index = 0; index < count; ++index) {
caryclark@google.coma5764232012-03-28 16:20:21 +00001997 SkScalar yIntercept;
1998 switch (wt.verb()) {
1999 case SkPath::kLine_Verb: {
2000 yIntercept = LineYAtT(wt.fPts, fTs[index]);
2001 break;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002002 }
caryclark@google.coma5764232012-03-28 16:20:21 +00002003 case SkPath::kQuad_Verb: {
2004 yIntercept = QuadYAtT(wt.fPts, fTs[index]);
2005 break;
2006 }
2007 case SkPath::kCubic_Verb: {
2008 yIntercept = CubicYAtT(wt.fPts, fTs[index]);
2009 break;
2010 }
2011 default:
2012 SkASSERT(0); // should never get here
2013 }
2014 if (yIntercept > y && bottom > yIntercept) {
2015 bottom = yIntercept;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002016 }
2017 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002018 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002019 }
caryclark@google.com198e0542012-03-30 18:47:02 +00002020#if DEBUG_BOTTOM
2021 SkDebugf("%s bottom=%1.9g\n", __FUNCTION__, bottom);
2022#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002023 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002024}
2025
2026static SkScalar findBottom(InEdge** currentPtr,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002027 InEdge** edgeListEnd, SkTDArray<ActiveEdge>* activeEdges, SkScalar y,
caryclark@google.com6008c6562012-02-15 22:01:16 +00002028 bool asFill, InEdge**& testPtr) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002029 InEdge* current = *currentPtr;
2030 SkScalar bottom = current->fBounds.fBottom;
caryclark@google.com752b60e2012-03-22 21:11:17 +00002031
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002032 // find the list of edges that cross y
caryclark@google.com6008c6562012-02-15 22:01:16 +00002033 InEdge* test = *testPtr;
2034 while (testPtr != edgeListEnd) {
2035 SkScalar testTop = test->fBounds.fTop;
2036 if (bottom <= testTop) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002037 break;
2038 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002039 SkScalar testBottom = test->fBounds.fBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002040 // OPTIMIZATION: Shortening the bottom is only interesting when filling
2041 // and when the edge is to the left of a longer edge. If it's a framing
2042 // edge, or part of the right, it won't effect the longer edges.
caryclark@google.com6008c6562012-02-15 22:01:16 +00002043 if (testTop > y) {
2044 bottom = testTop;
2045 break;
2046 }
2047 if (y < testBottom) {
2048 if (bottom > testBottom) {
2049 bottom = testBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002050 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002051 if (activeEdges) {
2052 addToActive(*activeEdges, test);
2053 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002054 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002055 test = *++testPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002056 }
caryclark@google.com198e0542012-03-30 18:47:02 +00002057#if DEBUG_BOTTOM
2058 SkDebugf("%s %d bottom=%1.9g\n", __FUNCTION__, activeEdges ? 2 : 1, bottom);
2059#endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002060 return bottom;
2061}
2062
2063static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
2064 SkTDArray<InEdge*>& edgeList) {
2065 size_t edgeCount = edges.count();
2066 if (edgeCount == 0) {
2067 return;
2068 }
caryclark@google.comfb173422012-04-10 18:28:55 +00002069 int id = 0;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002070 for (size_t index = 0; index < edgeCount; ++index) {
caryclark@google.comfb173422012-04-10 18:28:55 +00002071 InEdge& edge = edges[index];
2072 if (!edge.fWinding) {
2073 continue;
2074 }
2075 edge.fID = ++id;
2076 *edgeList.append() = &edge;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002077 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002078 *edgeList.append() = &edgeSentinel;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002079 QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002080}
2081
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002082static void makeHorizontalList(SkTDArray<HorizontalEdge>& edges,
2083 HorizontalEdge& edgeSentinel, SkTDArray<HorizontalEdge*>& edgeList) {
2084 size_t edgeCount = edges.count();
2085 if (edgeCount == 0) {
2086 return;
2087 }
2088 for (size_t index = 0; index < edgeCount; ++index) {
2089 *edgeList.append() = &edges[index];
2090 }
2091 edgeSentinel.fLeft = edgeSentinel.fRight = edgeSentinel.fY = SK_ScalarMax;
2092 *edgeList.append() = &edgeSentinel;
2093 QSort<HorizontalEdge>(edgeList.begin(), edgeList.end() - 1);
2094}
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002095
2096static void skipCoincidence(int lastWinding, int winding, int windingMask,
2097 ActiveEdge* activePtr, ActiveEdge* firstCoincident) {
2098 if (((lastWinding & windingMask) == 0) ^ (winding & windingMask) != 0) {
2099 return;
2100 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002101 // FIXME: ? shouldn't this be if (lastWinding & windingMask) ?
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002102 if (lastWinding) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002103#if DEBUG_ADJUST_COINCIDENT
2104 SkDebugf("%s edge=%d 1 set skip=false\n", __FUNCTION__, activePtr->ID());
2105#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002106 activePtr->fSkip = false;
2107 } else {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002108#if DEBUG_ADJUST_COINCIDENT
2109 SkDebugf("%s edge=%d 2 set skip=false\n", __FUNCTION__, firstCoincident->ID());
2110#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002111 firstCoincident->fSkip = false;
2112 }
2113}
2114
caryclark@google.com6008c6562012-02-15 22:01:16 +00002115static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002116 SkTDArray<ActiveEdge*>& edgeList, SkScalar y) {
caryclark@google.com6008c6562012-02-15 22:01:16 +00002117 size_t edgeCount = activeEdges.count();
2118 if (edgeCount == 0) {
2119 return;
2120 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002121#if DEBUG_SORT_HORIZONTAL
caryclark@google.comd88e0892012-03-27 13:23:51 +00002122 const int tab = 3; // FIXME: debugging only
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002123 SkDebugf("%s y=%1.9g\n", __FUNCTION__, y);
2124#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00002125 size_t index;
2126 for (index = 0; index < edgeCount; ++index) {
2127 ActiveEdge& activeEdge = activeEdges[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002128 do {
2129 activeEdge.calcLeft(y);
2130 // skip segments that don't span y
2131 if (activeEdge.fAbove != activeEdge.fBelow) {
2132 break;
2133 }
2134 if (activeEdge.fDone) {
2135#if DEBUG_SORT_HORIZONTAL
2136 SkDebugf("%*s edge=%d done\n", tab, "", activeEdge.ID());
2137#endif
2138 goto nextEdge;
2139 }
2140#if DEBUG_SORT_HORIZONTAL
2141 SkDebugf("%*s edge=%d above==below\n", tab, "", activeEdge.ID());
2142#endif
2143 } while (activeEdge.advanceT() || activeEdge.advance());
2144#if DEBUG_SORT_HORIZONTAL
2145 SkDebugf("%*s edge=%d above=(%1.9g,%1.9g) (%1.9g) below=(%1.9g,%1.9g)"
2146 " (%1.9g)\n", tab, "", activeEdge.ID(),
2147 activeEdge.fAbove.fX, activeEdge.fAbove.fY, activeEdge.fTAbove,
2148 activeEdge.fBelow.fX, activeEdge.fBelow.fY, activeEdge.fTBelow);
2149#endif
2150 activeEdge.fSkip = activeEdge.fCloseCall = activeEdge.fFixBelow = false;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002151 *edgeList.append() = &activeEdge;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002152nextEdge:
2153 ;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002154 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002155 QSort<ActiveEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002156}
2157
2158// remove coincident edges
2159// OPTIMIZE: remove edges? This is tricky because the current logic expects
2160// the winding count to be maintained while skipping coincident edges. In
2161// addition to removing the coincident edges, the remaining edges would need
2162// to have a different winding value, possibly different per intercept span.
2163static SkScalar adjustCoincident(SkTDArray<ActiveEdge*>& edgeList,
2164 int windingMask, SkScalar y, SkScalar bottom, OutEdgeBuilder& outBuilder)
2165{
2166#if DEBUG_ADJUST_COINCIDENT
2167 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
2168#endif
2169 size_t edgeCount = edgeList.count();
2170 if (edgeCount == 0) {
2171 return bottom;
2172 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002173 ActiveEdge* activePtr = edgeList[0];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002174 size_t index;
2175 bool foundCoincident = false;
2176 int firstIndex = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002177 for (index = 1; index < edgeCount; ++index) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002178 ActiveEdge* nextPtr = edgeList[index];
2179 bool closeCall = false;
2180 activePtr->fCoincident = firstIndex;
2181 if (activePtr->isCoincidentWith(nextPtr, y)
2182 || (closeCall = activePtr->tooCloseToCall(nextPtr))) {
2183 activePtr->fSkip = nextPtr->fSkip = foundCoincident = true;
2184 activePtr->fCloseCall = nextPtr->fCloseCall = closeCall;
2185 } else {
2186 firstIndex = index;
2187 }
2188 activePtr = nextPtr;
2189 }
2190 activePtr->fCoincident = firstIndex;
2191 if (!foundCoincident) {
2192 return bottom;
2193 }
2194 int winding = 0;
2195 activePtr = edgeList[0];
2196 for (index = 1; index < edgeCount; ++index) {
2197 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002198 winding += activePtr->fWorkEdge.winding();
caryclark@google.com6008c6562012-02-15 22:01:16 +00002199 ActiveEdge* nextPtr = edgeList[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002200 if (activePtr->fCoincident == nextPtr->fCoincident) {
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002201 // the coincident edges may not have been sorted above -- advance
2202 // the edges and resort if needed
2203 // OPTIMIZE: if sorting is done incrementally as new edges are added
2204 // and not all at once as is done here, fold this test into the
2205 // current less than test.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002206 if (activePtr->fCloseCall ? activePtr->swapClose(nextPtr,
2207 priorWinding, winding, windingMask)
2208 : activePtr->swapCoincident(nextPtr, bottom)) {
caryclark@google.com4917f172012-03-05 22:01:21 +00002209 winding -= activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002210 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
2211 SkTSwap<ActiveEdge*>(activePtr, nextPtr);
caryclark@google.com4917f172012-03-05 22:01:21 +00002212 winding += activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002213 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002214 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002215 activePtr = nextPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002216 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002217 int firstCoincidentWinding = 0;
2218 ActiveEdge* firstCoincident = NULL;
2219 winding = 0;
2220 activePtr = edgeList[0];
2221 for (index = 1; index < edgeCount; ++index) {
2222 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002223 winding += activePtr->fWorkEdge.winding();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002224 ActiveEdge* nextPtr = edgeList[index];
2225 if (activePtr->fCoincident == nextPtr->fCoincident) {
2226 if (!firstCoincident) {
2227 firstCoincident = activePtr;
2228 firstCoincidentWinding = priorWinding;
2229 }
2230 if (activePtr->fCloseCall) {
2231 // If one of the edges has already been added to out as a non
2232 // coincident edge, trim it back to the top of this span
2233 if (outBuilder.trimLine(y, activePtr->ID())) {
2234 activePtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002235 #if DEBUG_ADJUST_COINCIDENT
2236 SkDebugf("%s 1 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
2237 __FUNCTION__, activePtr->ID(), y, activePtr->fYBottom);
2238 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002239 activePtr->fYBottom = y;
2240 }
2241 if (outBuilder.trimLine(y, nextPtr->ID())) {
2242 nextPtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002243 #if DEBUG_ADJUST_COINCIDENT
2244 SkDebugf("%s 2 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
2245 __FUNCTION__, nextPtr->ID(), y, nextPtr->fYBottom);
2246 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002247 nextPtr->fYBottom = y;
2248 }
2249 // add missing t values so edges can be the same length
2250 SkScalar testY = activePtr->fBelow.fY;
2251 nextPtr->addTatYBelow(testY);
2252 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002253 #if DEBUG_ADJUST_COINCIDENT
2254 SkDebugf("%s 3 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
2255 __FUNCTION__, activePtr->ID(), testY, bottom);
2256 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002257 bottom = testY;
2258 }
2259 testY = nextPtr->fBelow.fY;
2260 activePtr->addTatYBelow(testY);
2261 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002262 #if DEBUG_ADJUST_COINCIDENT
2263 SkDebugf("%s 4 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
2264 __FUNCTION__, nextPtr->ID(), testY, bottom);
2265 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002266 bottom = testY;
2267 }
2268 }
2269 } else if (firstCoincident) {
2270 skipCoincidence(firstCoincidentWinding, winding, windingMask,
2271 activePtr, firstCoincident);
2272 firstCoincident = NULL;
2273 }
2274 activePtr = nextPtr;
2275 }
2276 if (firstCoincident) {
2277 winding += activePtr->fWorkEdge.winding();
2278 skipCoincidence(firstCoincidentWinding, winding, windingMask, activePtr,
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002279 firstCoincident);
2280 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002281 // fix up the bottom for close call edges. OPTIMIZATION: maybe this could
2282 // be in the loop above, but moved here since loop above reads fBelow and
2283 // it felt unsafe to write it in that loop
2284 for (index = 0; index < edgeCount; ++index) {
2285 (edgeList[index])->fixBelow();
2286 }
2287 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002288}
2289
2290// stitch edge and t range that satisfies operation
caryclark@google.com6008c6562012-02-15 22:01:16 +00002291static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
caryclark@google.com752b60e2012-03-22 21:11:17 +00002292 SkScalar bottom, int windingMask, bool fill, OutEdgeBuilder& outBuilder) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002293 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002294 ActiveEdge** activeHandle = edgeList.begin() - 1;
2295 ActiveEdge** lastActive = edgeList.end();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002296 const int tab = 7; // FIXME: debugging only
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002297 if (gShowDebugf) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002298 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002299 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002300 while (++activeHandle != lastActive) {
2301 ActiveEdge* activePtr = *activeHandle;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002302 const WorkEdge& wt = activePtr->fWorkEdge;
2303 int lastWinding = winding;
2304 winding += wt.winding();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002305 if (gShowDebugf) {
2306 SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d"
2307#if DEBUG_ABOVE_BELOW
2308 " above=%1.9g below=%1.9g"
2309#endif
2310 "\n",
2311 tab-4, "", activePtr->ID(), lastWinding,
2312 winding, activePtr->fSkip, activePtr->fCloseCall
2313#if DEBUG_ABOVE_BELOW
2314 , activePtr->fTAbove, activePtr->fTBelow
2315#endif
2316 );
2317 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00002318 if (activePtr->done(bottom)) {
2319 if (gShowDebugf) {
2320 SkDebugf("%*s fDone=%d || fYBottom=%1.9g >= bottom\n", tab, "",
2321 activePtr->fDone, activePtr->fYBottom);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002322 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00002323 continue;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002324 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00002325 int opener = (lastWinding & windingMask) == 0;
2326 bool closer = (winding & windingMask) == 0;
2327 SkASSERT(!opener | !closer);
2328 bool inWinding = opener | closer;
caryclark@google.coma5764232012-03-28 16:20:21 +00002329 SkPoint clippedPts[4];
caryclark@google.com4917f172012-03-05 22:01:21 +00002330 const SkPoint* clipped = NULL;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002331 uint8_t verb = wt.verb();
2332 bool moreToDo, aboveBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002333 do {
2334 double currentT = activePtr->t();
2335 const SkPoint* points = wt.fPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002336 double nextT;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002337 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002338 nextT = activePtr->nextT();
caryclark@google.coma5764232012-03-28 16:20:21 +00002339 // FIXME: obtuse: want efficient way to say
2340 // !currentT && currentT != 1 || !nextT && nextT != 1
2341 if (currentT * nextT != 0 || currentT + nextT != 1) {
2342 // OPTIMIZATION: if !inWinding, we only need
2343 // clipped[1].fY
2344 switch (verb) {
2345 case SkPath::kLine_Verb:
2346 LineSubDivide(points, currentT, nextT, clippedPts);
2347 break;
2348 case SkPath::kQuad_Verb:
2349 QuadSubDivide(points, currentT, nextT, clippedPts);
2350 break;
2351 case SkPath::kCubic_Verb:
2352 CubicSubDivide(points, currentT, nextT, clippedPts);
2353 break;
2354 default:
2355 SkASSERT(0);
2356 break;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002357 }
caryclark@google.coma5764232012-03-28 16:20:21 +00002358 clipped = clippedPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002359 } else {
caryclark@google.coma5764232012-03-28 16:20:21 +00002360 clipped = points;
2361 }
2362 if (inWinding && !activePtr->fSkip && (fill ? clipped[0].fY
2363 != clipped[verb].fY : clipped[0] != clipped[verb])) {
caryclark@google.comfb173422012-04-10 18:28:55 +00002364#if DEBUG_STITCH_EDGE
2365 SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d"
2366 " v=%d t=(%1.9g,%1.9g)\n", tab, "",
2367 kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
2368 clipped[verb].fX, clipped[verb].fY,
2369 activePtr->ID(),
2370 activePtr->fWorkEdge.fVerb
2371 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
2372 currentT, nextT);
2373#endif
caryclark@google.coma5764232012-03-28 16:20:21 +00002374 outBuilder.addCurve(clipped, (SkPath::Verb) verb,
2375 activePtr->fWorkEdge.fEdge->fID,
2376 activePtr->fCloseCall);
2377 } else {
caryclark@google.comfb173422012-04-10 18:28:55 +00002378#if DEBUG_STITCH_EDGE
2379 SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g"
2380 " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
2381 kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
2382 clipped[verb].fX, clipped[verb].fY,
2383 activePtr->ID(),
2384 activePtr->fWorkEdge.fVerb
2385 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
2386 currentT, nextT);
2387#endif
caryclark@google.coma5764232012-03-28 16:20:21 +00002388 }
2389 // by advancing fAbove/fBelow, the next call to sortHorizontal
2390 // will use these values if they're still valid instead of
2391 // recomputing
2392 if (clipped[1].fY > activePtr->fBelow.fY
2393 && bottom >= activePtr->fBelow.fY ) {
2394 activePtr->fAbove = activePtr->fBelow;
2395 activePtr->fBelow = clipped[1];
2396 #if DEBUG_ABOVE_BELOW
2397 activePtr->fTAbove = activePtr->fTBelow;
2398 activePtr->fTBelow = nextT;
2399 #endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002400 }
2401 currentT = nextT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002402 moreToDo = activePtr->advanceT();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002403 activePtr->fYBottom = clipped[verb].fY; // was activePtr->fCloseCall ? bottom :
2404
2405 // clearing the fSkip/fCloseCall bit here means that trailing edges
2406 // fall out of sync, if one edge is long and another is a series of short pieces
2407 // if fSkip/fCloseCall is set, need to recompute coincidence/too-close-to-call
2408 // after advancing
2409 // another approach would be to restrict bottom to smaller part of close call
2410 // maybe this is already happening with coincidence when intersection is computed,
2411 // and needs to be added to the close call computation as well
2412 // this is hard to do because that the bottom is important is not known when
2413 // the lines are intersected; only when the computation for edge sorting is done
2414 // does the need for new bottoms become apparent.
2415 // maybe this is good incentive to scrap the current sort and do an insertion
2416 // sort that can take this into consideration when the x value is computed
2417
2418 // FIXME: initialized in sortHorizontal, cleared here as well so
2419 // that next edge is not skipped -- but should skipped edges ever
2420 // continue? (probably not)
caryclark@google.com752b60e2012-03-22 21:11:17 +00002421 aboveBottom = clipped[verb].fY < bottom;
2422 if (clipped[0].fY != clipped[verb].fY) {
2423 activePtr->fSkip = false;
2424 activePtr->fCloseCall = false;
2425 aboveBottom &= !activePtr->fCloseCall;
2426 } else {
2427 if (activePtr->fSkip || activePtr->fCloseCall) {
caryclark@google.com198e0542012-03-30 18:47:02 +00002428 if (gShowDebugf) SkDebugf("== %1.9g\n", clippedPts[0].fY);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002429 }
2430 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002431 } while (moreToDo & aboveBottom);
2432 } while ((moreToDo || activePtr->advance()) & aboveBottom);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002433 }
2434}
2435
caryclark@google.com198e0542012-03-30 18:47:02 +00002436static void dumpEdgeList(const SkTDArray<InEdge*>& edgeList,
2437 const InEdge& edgeSentinel) {
2438#if DEBUG_DUMP
2439 InEdge** debugPtr = edgeList.begin();
2440 do {
2441 (*debugPtr++)->dump();
2442 } while (*debugPtr != &edgeSentinel);
2443#endif
2444}
2445
caryclark@google.comc6825902012-02-03 22:07:47 +00002446void simplify(const SkPath& path, bool asFill, SkPath& simple) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002447 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
2448 int windingMask = (path.getFillType() & 1) ? 1 : -1;
2449 simple.reset();
2450 simple.setFillType(SkPath::kEvenOdd_FillType);
caryclark@google.comc6825902012-02-03 22:07:47 +00002451 // turn path into list of edges increasing in y
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002452 // if an edge is a quad or a cubic with a y extrema, note it, but leave it
2453 // unbroken. Once we have a list, sort it, then walk the list (walk edges
2454 // twice that have y extrema's on top) and detect crossings -- look for raw
2455 // bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +00002456 SkTArray<InEdge> edges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002457 SkTDArray<HorizontalEdge> horizontalEdges;
2458 InEdgeBuilder builder(path, asFill, edges, horizontalEdges);
caryclark@google.com6680fb12012-02-07 22:10:51 +00002459 SkTDArray<InEdge*> edgeList;
caryclark@google.com6680fb12012-02-07 22:10:51 +00002460 InEdge edgeSentinel;
caryclark@google.comfb173422012-04-10 18:28:55 +00002461 edgeSentinel.reset();
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002462 makeEdgeList(edges, edgeSentinel, edgeList);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002463 SkTDArray<HorizontalEdge*> horizontalList;
2464 HorizontalEdge horizontalSentinel;
2465 makeHorizontalList(horizontalEdges, horizontalSentinel, horizontalList);
caryclark@google.com6680fb12012-02-07 22:10:51 +00002466 InEdge** currentPtr = edgeList.begin();
caryclark@google.com4917f172012-03-05 22:01:21 +00002467 if (!currentPtr) {
2468 return;
2469 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002470 // find all intersections between edges
2471// beyond looking for horizontal intercepts, we need to know if any active edges
2472// intersect edges below 'bottom', but above the active edge segment.
2473// maybe it makes more sense to compute all intercepts before doing anything
2474// else, since the intercept list is long-lived, at least in the current design.
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002475 SkScalar y = (*currentPtr)->fBounds.fTop;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002476 HorizontalEdge** currentHorizontal = horizontalList.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00002477 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +00002478 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002479 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002480 NULL, y, asFill, lastPtr);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002481 if (lastPtr > currentPtr) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002482 if (currentHorizontal) {
2483 if ((*currentHorizontal)->fY < SK_ScalarMax) {
2484 addBottomT(currentPtr, lastPtr, currentHorizontal);
2485 }
2486 currentHorizontal = advanceHorizontal(currentHorizontal, bottom);
2487 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00002488 addIntersectingTs(currentPtr, lastPtr);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002489 }
2490 y = bottom;
2491 currentPtr = advanceEdges(NULL, currentPtr, lastPtr, y);
2492 } while (*currentPtr != &edgeSentinel);
caryclark@google.com198e0542012-03-30 18:47:02 +00002493 // if a quadratic or cubic now has an intermediate T value, see if the Ts
2494 // on either side cause the Y values to monotonically increase. If not, split
2495 // the curve at the new T.
2496 if (builder.containsCurves()) {
2497 currentPtr = edgeList.begin();
2498 SkTArray<InEdge> splits;
2499 do {
caryclark@google.comfb173422012-04-10 18:28:55 +00002500 (*currentPtr)->splitInflectionPts(splits);
caryclark@google.com198e0542012-03-30 18:47:02 +00002501 } while (*++currentPtr != &edgeSentinel);
2502 if (splits.count()) {
caryclark@google.com198e0542012-03-30 18:47:02 +00002503 for (int index = 0; index < splits.count(); ++index) {
2504 edges.push_back(splits[index]);
2505 }
caryclark@google.comfb173422012-04-10 18:28:55 +00002506 edgeList.reset();
caryclark@google.com198e0542012-03-30 18:47:02 +00002507 makeEdgeList(edges, edgeSentinel, edgeList);
2508 }
2509 }
2510 dumpEdgeList(edgeList, edgeSentinel);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002511 // walk the sorted edges from top to bottom, computing accumulated winding
2512 SkTDArray<ActiveEdge> activeEdges;
2513 OutEdgeBuilder outBuilder(asFill);
2514 currentPtr = edgeList.begin();
2515 y = (*currentPtr)->fBounds.fTop;
2516 do {
2517 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
2518 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
2519 &activeEdges, y, asFill, lastPtr);
2520 if (lastPtr > currentPtr) {
2521 bottom = computeInterceptBottom(activeEdges, y, bottom);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002522 SkTDArray<ActiveEdge*> activeEdgeList;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002523 sortHorizontal(activeEdges, activeEdgeList, y);
2524 bottom = adjustCoincident(activeEdgeList, windingMask, y, bottom,
2525 outBuilder);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002526 stitchEdge(activeEdgeList, y, bottom, windingMask, asFill, outBuilder);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002527 }
caryclark@google.comc6825902012-02-03 22:07:47 +00002528 y = bottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002529 // OPTIMIZATION: as edges expire, InEdge allocations could be released
2530 currentPtr = advanceEdges(&activeEdges, currentPtr, lastPtr, y);
caryclark@google.comc6825902012-02-03 22:07:47 +00002531 } while (*currentPtr != &edgeSentinel);
caryclark@google.comc6825902012-02-03 22:07:47 +00002532 // assemble output path from string of pts, verbs
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002533 outBuilder.bridge();
2534 outBuilder.assemble(simple);
caryclark@google.comc6825902012-02-03 22:07:47 +00002535}