blob: 002e84c5995db53df9efbee76b9fc840fce2d64c [file] [log] [blame]
caryclark@google.comc6825902012-02-03 22:07:47 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
caryclark@google.com6680fb12012-02-07 22:10:51 +00008#include "CurveIntersection.h"
caryclark@google.coma5764232012-03-28 16:20:21 +00009#include "Intersections.h"
caryclark@google.comc6825902012-02-03 22:07:47 +000010#include "LineIntersection.h"
11#include "SkPath.h"
12#include "SkRect.h"
13#include "SkTArray.h"
14#include "SkTDArray.h"
caryclark@google.comd88e0892012-03-27 13:23:51 +000015#include "ShapeOps.h"
caryclark@google.comc6825902012-02-03 22:07:47 +000016#include "TSearch.h"
17
caryclark@google.com78e17132012-04-17 11:40:34 +000018#undef SkASSERT
19#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000020
caryclark@google.com78e17132012-04-17 11:40:34 +000021// FIXME: remove once debugging is complete
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022#if 01 // set to 1 for no debugging whatsoever
caryclark@google.com78e17132012-04-17 11:40:34 +000023
caryclark@google.comfa0588f2012-04-26 21:01:06 +000024const bool gRunTestsInOneThread = false;
caryclark@google.com78e17132012-04-17 11:40:34 +000025
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000026#define DEBUG_ACTIVE_LESS_THAN 0
caryclark@google.com78e17132012-04-17 11:40:34 +000027#define DEBUG_ADD 0
28#define DEBUG_ADD_BOTTOM_TS 0
29#define DEBUG_ADD_INTERSECTING_TS 0
30#define DEBUG_ADJUST_COINCIDENT 0
caryclark@google.comfb173422012-04-10 18:28:55 +000031#define DEBUG_ASSEMBLE 0
caryclark@google.com78e17132012-04-17 11:40:34 +000032#define DEBUG_BOTTOM 0
caryclark@google.comfb173422012-04-10 18:28:55 +000033#define DEBUG_BRIDGE 0
caryclark@google.com78e17132012-04-17 11:40:34 +000034#define DEBUG_DUMP 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000035#define DEBUG_SORT_HORIZONTAL 0
36#define DEBUG_OUT 0
37#define DEBUG_OUT_LESS_THAN 0
caryclark@google.com198e0542012-03-30 18:47:02 +000038#define DEBUG_SPLIT 0
caryclark@google.comfb173422012-04-10 18:28:55 +000039#define DEBUG_STITCH_EDGE 0
caryclark@google.com78e17132012-04-17 11:40:34 +000040#define DEBUG_TRIM_LINE 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000041
caryclark@google.com78e17132012-04-17 11:40:34 +000042#else
43
44const bool gRunTestsInOneThread = true;
45
caryclark@google.coma5764232012-03-28 16:20:21 +000046#define DEBUG_ACTIVE_LESS_THAN 0
caryclark@google.com78e17132012-04-17 11:40:34 +000047#define DEBUG_ADD 01
48#define DEBUG_ADD_BOTTOM_TS 0
49#define DEBUG_ADD_INTERSECTING_TS 0
50#define DEBUG_ADJUST_COINCIDENT 1
caryclark@google.comfb173422012-04-10 18:28:55 +000051#define DEBUG_ASSEMBLE 1
caryclark@google.com78e17132012-04-17 11:40:34 +000052#define DEBUG_BOTTOM 0
caryclark@google.comfb173422012-04-10 18:28:55 +000053#define DEBUG_BRIDGE 1
caryclark@google.com78e17132012-04-17 11:40:34 +000054#define DEBUG_DUMP 1
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000055#define DEBUG_SORT_HORIZONTAL 01
56#define DEBUG_OUT 01
57#define DEBUG_OUT_LESS_THAN 0
caryclark@google.com198e0542012-03-30 18:47:02 +000058#define DEBUG_SPLIT 1
caryclark@google.comfb173422012-04-10 18:28:55 +000059#define DEBUG_STITCH_EDGE 1
caryclark@google.com78e17132012-04-17 11:40:34 +000060#define DEBUG_TRIM_LINE 1
caryclark@google.com752b60e2012-03-22 21:11:17 +000061
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000062#endif
63
caryclark@google.comfb173422012-04-10 18:28:55 +000064#if DEBUG_ASSEMBLE || DEBUG_BRIDGE
65static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
66#endif
67#if DEBUG_STITCH_EDGE
68static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
69#endif
70
caryclark@google.com6680fb12012-02-07 22:10:51 +000071static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
caryclark@google.coma5764232012-03-28 16:20:21 +000072 Intersections& intersections) {
73 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
74 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
75 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
76}
77
78static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
79 Intersections& intersections) {
80 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
81 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +000082 intersect(aQuad, bLine, intersections);
83 return intersections.fUsed;
caryclark@google.coma5764232012-03-28 16:20:21 +000084}
85
86static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
87 Intersections& intersections) {
88 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
89 {a[3].fX, a[3].fY}};
90 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +000091 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
caryclark@google.coma5764232012-03-28 16:20:21 +000092}
93
94static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
95 Intersections& intersections) {
96 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
97 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 +000098 intersect(aQuad, bQuad, intersections);
99 return intersections.fUsed;
caryclark@google.coma5764232012-03-28 16:20:21 +0000100}
101
102static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
103 Intersections& intersections) {
104 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
105 {a[3].fX, a[3].fY}};
106 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
107 {b[3].fX, b[3].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +0000108 intersect(aCubic, bCubic, intersections);
109 return intersections.fUsed;
caryclark@google.comc6825902012-02-03 22:07:47 +0000110}
111
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000112static int LineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
113 SkScalar y, double aRange[2]) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000114 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000115 return horizontalLineIntersect(aLine, left, right, y, aRange);
caryclark@google.comc6825902012-02-03 22:07:47 +0000116}
117
caryclark@google.com198e0542012-03-30 18:47:02 +0000118static int QuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
119 SkScalar y, double aRange[3]) {
120 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
121 return horizontalIntersect(aQuad, left, right, y, aRange);
122}
123
124static int CubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
125 SkScalar y, double aRange[4]) {
126 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
127 {a[3].fX, a[3].fY}};
128 return horizontalIntersect(aCubic, left, right, y, aRange);
129}
130
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000131static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000132 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000133 double x, y;
caryclark@google.coma5764232012-03-28 16:20:21 +0000134 xy_at_t(line, t, x, y);
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000135 out->fX = SkDoubleToScalar(x);
136 out->fY = SkDoubleToScalar(y);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000137}
138
caryclark@google.coma5764232012-03-28 16:20:21 +0000139static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
140 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
141 double x, y;
142 xy_at_t(quad, t, x, y);
143 out->fX = SkDoubleToScalar(x);
144 out->fY = SkDoubleToScalar(y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000145}
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000146
caryclark@google.coma5764232012-03-28 16:20:21 +0000147static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
148 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
149 {a[3].fX, a[3].fY}};
150 double x, y;
151 xy_at_t(cubic, t, x, y);
152 out->fX = SkDoubleToScalar(x);
153 out->fY = SkDoubleToScalar(y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000154}
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000155
caryclark@google.com6680fb12012-02-07 22:10:51 +0000156static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000157 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com6680fb12012-02-07 22:10:51 +0000158 double y;
159 xy_at_t(aLine, t, *(double*) 0, y);
160 return SkDoubleToScalar(y);
161}
162
caryclark@google.coma5764232012-03-28 16:20:21 +0000163static SkScalar QuadYAtT(const SkPoint a[3], double t) {
164 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
165 double y;
166 xy_at_t(quad, t, *(double*) 0, y);
167 return SkDoubleToScalar(y);
168}
169
170static SkScalar CubicYAtT(const SkPoint a[4], double t) {
171 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
172 {a[3].fX, a[3].fY}};
173 double y;
174 xy_at_t(cubic, t, *(double*) 0, y);
175 return SkDoubleToScalar(y);
176}
177
caryclark@google.com6680fb12012-02-07 22:10:51 +0000178static void LineSubDivide(const SkPoint a[2], double startT, double endT,
179 SkPoint sub[2]) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000180 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com6680fb12012-02-07 22:10:51 +0000181 _Line dst;
182 sub_divide(aLine, startT, endT, dst);
183 sub[0].fX = SkDoubleToScalar(dst[0].x);
184 sub[0].fY = SkDoubleToScalar(dst[0].y);
185 sub[1].fX = SkDoubleToScalar(dst[1].x);
186 sub[1].fY = SkDoubleToScalar(dst[1].y);
187}
188
caryclark@google.coma5764232012-03-28 16:20:21 +0000189static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
190 SkPoint sub[3]) {
caryclark@google.com78e17132012-04-17 11:40:34 +0000191 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
192 {a[2].fX, a[2].fY}};
caryclark@google.coma5764232012-03-28 16:20:21 +0000193 Quadratic dst;
194 sub_divide(aQuad, startT, endT, dst);
195 sub[0].fX = SkDoubleToScalar(dst[0].x);
196 sub[0].fY = SkDoubleToScalar(dst[0].y);
197 sub[1].fX = SkDoubleToScalar(dst[1].x);
198 sub[1].fY = SkDoubleToScalar(dst[1].y);
199 sub[2].fX = SkDoubleToScalar(dst[2].x);
200 sub[2].fY = SkDoubleToScalar(dst[2].y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000201}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000202
caryclark@google.coma5764232012-03-28 16:20:21 +0000203static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
204 SkPoint sub[4]) {
caryclark@google.com78e17132012-04-17 11:40:34 +0000205 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
206 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
caryclark@google.coma5764232012-03-28 16:20:21 +0000207 Cubic dst;
208 sub_divide(aCubic, startT, endT, dst);
209 sub[0].fX = SkDoubleToScalar(dst[0].x);
210 sub[0].fY = SkDoubleToScalar(dst[0].y);
211 sub[1].fX = SkDoubleToScalar(dst[1].x);
212 sub[1].fY = SkDoubleToScalar(dst[1].y);
213 sub[2].fX = SkDoubleToScalar(dst[2].x);
214 sub[2].fY = SkDoubleToScalar(dst[2].y);
215 sub[3].fX = SkDoubleToScalar(dst[3].x);
216 sub[3].fY = SkDoubleToScalar(dst[3].y);
217}
caryclark@google.comfb173422012-04-10 18:28:55 +0000218
219static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
220 SkRect& bounds) {
221 SkPoint dst[3];
222 QuadSubDivide(a, startT, endT, dst);
223 bounds.fLeft = bounds.fRight = dst[0].fX;
224 bounds.fTop = bounds.fBottom = dst[0].fY;
225 for (int index = 1; index < 3; ++index) {
226 bounds.growToInclude(dst[index].fX, dst[index].fY);
227 }
228}
229
230static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
231 SkRect& bounds) {
232 SkPoint dst[4];
233 CubicSubDivide(a, startT, endT, dst);
234 bounds.fLeft = bounds.fRight = dst[0].fX;
235 bounds.fTop = bounds.fBottom = dst[0].fY;
236 for (int index = 1; index < 4; ++index) {
237 bounds.growToInclude(dst[index].fX, dst[index].fY);
238 }
239}
240
caryclark@google.com78e17132012-04-17 11:40:34 +0000241static SkPath::Verb QuadReduceOrder(SkPoint a[4]) {
242 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
243 {a[2].fX, a[2].fY}};
244 Quadratic dst;
245 int order = reduceOrder(aQuad, dst);
246 for (int index = 0; index < order; ++index) {
247 a[index].fX = SkDoubleToScalar(dst[index].x);
248 a[index].fY = SkDoubleToScalar(dst[index].y);
249 }
250 if (order == 1) { // FIXME: allow returning points, caller should discard
251 a[1] = a[0];
252 return (SkPath::Verb) order;
253 }
254 return (SkPath::Verb) (order - 1);
255}
256
257static SkPath::Verb CubicReduceOrder(SkPoint a[4]) {
258 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
259 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
260 Cubic dst;
261 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
262 for (int index = 0; index < order; ++index) {
263 a[index].fX = SkDoubleToScalar(dst[index].x);
264 a[index].fY = SkDoubleToScalar(dst[index].y);
265 }
266 if (order == 1) { // FIXME: allow returning points, caller should discard
267 a[1] = a[0];
268 return (SkPath::Verb) order;
269 }
270 return (SkPath::Verb) (order - 1);
271}
272
273static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
274 const SkPoint& below) {
275 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
276 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
277 return implicit_matches_ulps(aLine, bLine, 32);
278}
279
caryclark@google.comc6825902012-02-03 22:07:47 +0000280/*
281list of edges
282bounds for edge
283sort
284active T
285
286if a contour's bounds is outside of the active area, no need to create edges
287*/
288
289/* given one or more paths,
290 find the bounds of each contour, select the active contours
291 for each active contour, compute a set of edges
292 each edge corresponds to one or more lines and curves
293 leave edges unbroken as long as possible
294 when breaking edges, compute the t at the break but leave the control points alone
295
296 */
297
298void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray) {
299 SkPath::Iter iter(path, false);
300 SkPoint pts[4];
301 SkPath::Verb verb;
302 SkRect bounds;
303 bounds.setEmpty();
304 int count = 0;
305 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
306 switch (verb) {
307 case SkPath::kMove_Verb:
308 if (!bounds.isEmpty()) {
309 *boundsArray.append() = bounds;
310 }
311 bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
312 count = 0;
313 break;
314 case SkPath::kLine_Verb:
315 count = 1;
316 break;
317 case SkPath::kQuad_Verb:
318 count = 2;
319 break;
320 case SkPath::kCubic_Verb:
321 count = 3;
322 break;
323 case SkPath::kClose_Verb:
324 count = 0;
325 break;
326 default:
327 SkDEBUGFAIL("bad verb");
328 return;
329 }
330 for (int i = 1; i <= count; ++i) {
331 bounds.growToInclude(pts[i].fX, pts[i].fY);
332 }
333 }
334}
335
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000336static bool extendLine(const SkPoint line[2], const SkPoint& add) {
337 // FIXME: allow this to extend lines that have slopes that are nearly equal
338 SkScalar dx1 = line[1].fX - line[0].fX;
339 SkScalar dy1 = line[1].fY - line[0].fY;
340 SkScalar dx2 = add.fX - line[0].fX;
341 SkScalar dy2 = add.fY - line[0].fY;
342 return dx1 * dy2 == dx2 * dy1;
343}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000344
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000345// OPTIMIZATION: this should point to a list of input data rather than duplicating
346// the line data here. This would reduce the need to assemble the results.
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000347struct OutEdge {
348 bool operator<(const OutEdge& rh) const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000349 const SkPoint& first = fPts[0];
350 const SkPoint& rhFirst = rh.fPts[0];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000351 return first.fY == rhFirst.fY
352 ? first.fX < rhFirst.fX
353 : first.fY < rhFirst.fY;
354 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000355
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000356 SkPoint fPts[4];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000357 int fID; // id of edge generating data
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000358 uint8_t fVerb; // FIXME: not read from everywhere
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000359 bool fCloseCall; // edge is trimmable if not originally coincident
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000360};
361
caryclark@google.com6680fb12012-02-07 22:10:51 +0000362class OutEdgeBuilder {
363public:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000364 OutEdgeBuilder(bool fill)
365 : fFill(fill) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000366 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000367
caryclark@google.coma5764232012-03-28 16:20:21 +0000368 void addCurve(const SkPoint line[4], SkPath::Verb verb, int id,
369 bool closeCall) {
caryclark@google.comc17972e2012-02-20 21:33:22 +0000370 OutEdge& newEdge = fEdges.push_back();
caryclark@google.coma5764232012-03-28 16:20:21 +0000371 memcpy(newEdge.fPts, line, (verb + 1) * sizeof(SkPoint));
372 newEdge.fVerb = verb;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000373 newEdge.fID = id;
374 newEdge.fCloseCall = closeCall;
375 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000376
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000377 bool trimLine(SkScalar y, int id) {
378 size_t count = fEdges.count();
379 while (count-- != 0) {
380 OutEdge& edge = fEdges[count];
381 if (edge.fID != id) {
382 continue;
383 }
384 if (edge.fCloseCall) {
385 return false;
386 }
387 SkASSERT(edge.fPts[0].fY <= y);
388 if (edge.fPts[1].fY <= y) {
389 continue;
390 }
391 edge.fPts[1].fX = edge.fPts[0].fX + (y - edge.fPts[0].fY)
392 * (edge.fPts[1].fX - edge.fPts[0].fX)
393 / (edge.fPts[1].fY - edge.fPts[0].fY);
394 edge.fPts[1].fY = y;
caryclark@google.com78e17132012-04-17 11:40:34 +0000395#if DEBUG_TRIM_LINE
396 SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id,
397 edge.fPts[1].fX, y);
398#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000399 return true;
400 }
401 return false;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000402 }
403
404 void assemble(SkPath& simple) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000405 size_t listCount = fEdges.count();
406 if (listCount == 0) {
407 return;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000408 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000409 do {
410 size_t listIndex = 0;
411 int advance = 1;
412 while (listIndex < listCount && fTops[listIndex] == 0) {
413 ++listIndex;
414 }
415 if (listIndex >= listCount) {
416 break;
417 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000418 int closeEdgeIndex = -listIndex - 1;
caryclark@google.comfb173422012-04-10 18:28:55 +0000419 // the curve is deferred and not added right away because the
420 // following edge may extend the first curve.
caryclark@google.coma5764232012-03-28 16:20:21 +0000421 SkPoint firstPt, lastCurve[4];
422 uint8_t lastVerb;
caryclark@google.comfb173422012-04-10 18:28:55 +0000423#if DEBUG_ASSEMBLE
424 int firstIndex, lastIndex;
425 const int tab = 8;
426#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +0000427 bool doMove = true;
428 int edgeIndex;
429 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000430 SkPoint* ptArray = fEdges[listIndex].fPts;
431 uint8_t verb = fEdges[listIndex].fVerb;
caryclark@google.comfb173422012-04-10 18:28:55 +0000432 SkPoint* curve[4];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000433 if (advance < 0) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000434 curve[0] = &ptArray[verb];
435 if (verb == SkPath::kCubic_Verb) {
436 curve[1] = &ptArray[2];
437 curve[2] = &ptArray[1];
438 }
439 curve[verb] = &ptArray[0];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000440 } else {
caryclark@google.comfb173422012-04-10 18:28:55 +0000441 curve[0] = &ptArray[0];
442 if (verb == SkPath::kCubic_Verb) {
443 curve[1] = &ptArray[1];
444 curve[2] = &ptArray[2];
445 }
446 curve[verb] = &ptArray[verb];
447 }
448 if (verb == SkPath::kQuad_Verb) {
449 curve[1] = &ptArray[1];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000450 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000451 if (doMove) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000452 firstPt = *curve[0];
453 simple.moveTo(curve[0]->fX, curve[0]->fY);
454#if DEBUG_ASSEMBLE
455 SkDebugf("%s %d moveTo (%g,%g)\n", __FUNCTION__,
456 listIndex + 1, curve[0]->fX, curve[0]->fY);
457 firstIndex = listIndex;
458#endif
459 for (int index = 0; index <= verb; ++index) {
460 lastCurve[index] = *curve[index];
caryclark@google.coma5764232012-03-28 16:20:21 +0000461 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000462 doMove = false;
463 } else {
caryclark@google.comfb173422012-04-10 18:28:55 +0000464 bool gap = lastCurve[lastVerb] != *curve[0];
465 if (gap || lastVerb != SkPath::kLine_Verb) { // output the accumulated curve before the gap
caryclark@google.coma5764232012-03-28 16:20:21 +0000466 // FIXME: see comment in bridge -- this probably
467 // conceals errors
caryclark@google.comfb173422012-04-10 18:28:55 +0000468 SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY,
469 curve[0]->fY) <= 10);
caryclark@google.coma5764232012-03-28 16:20:21 +0000470 switch (lastVerb) {
471 case SkPath::kLine_Verb:
472 simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
473 break;
474 case SkPath::kQuad_Verb:
475 simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
476 lastCurve[2].fX, lastCurve[2].fY);
477 break;
478 case SkPath::kCubic_Verb:
479 simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
480 lastCurve[2].fX, lastCurve[2].fY,
481 lastCurve[3].fX, lastCurve[3].fY);
482 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000483 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000484#if DEBUG_ASSEMBLE
485 SkDebugf("%*s %d %sTo (%g,%g)\n", tab, "", lastIndex + 1,
486 kLVerbStr[lastVerb], lastCurve[lastVerb].fX,
487 lastCurve[lastVerb].fY);
488#endif
caryclark@google.coma5764232012-03-28 16:20:21 +0000489 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000490 int firstCopy = 1;
caryclark@google.com78e17132012-04-17 11:40:34 +0000491 if (gap || (lastVerb == SkPath::kLine_Verb
492 && (verb != SkPath::kLine_Verb
493 || !extendLine(lastCurve, *curve[verb])))) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000494 // FIXME: see comment in bridge -- this probably
495 // conceals errors
caryclark@google.comfb173422012-04-10 18:28:55 +0000496 SkASSERT(lastCurve[lastVerb] == *curve[0] ||
497 (fFill && UlpsDiff(lastCurve[lastVerb].fY,
498 curve[0]->fY) <= 10));
499 simple.lineTo(curve[0]->fX, curve[0]->fY);
500#if DEBUG_ASSEMBLE
501 SkDebugf("%*s %d gap lineTo (%g,%g)\n", tab, "",
502 lastIndex + 1, curve[0]->fX, curve[0]->fY);
503#endif
504 firstCopy = 0;
505 } else if (lastVerb != SkPath::kLine_Verb) {
506 firstCopy = 0;
caryclark@google.coma5764232012-03-28 16:20:21 +0000507 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000508 for (int index = firstCopy; index <= verb; ++index) {
509 lastCurve[index] = *curve[index];
caryclark@google.coma5764232012-03-28 16:20:21 +0000510 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000511 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000512 lastVerb = verb;
513#if DEBUG_ASSEMBLE
514 lastIndex = listIndex;
515#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +0000516 if (advance < 0) {
517 edgeIndex = fTops[listIndex];
518 fTops[listIndex] = 0;
caryclark@google.comfb173422012-04-10 18:28:55 +0000519 } else {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000520 edgeIndex = fBottoms[listIndex];
521 fBottoms[listIndex] = 0;
522 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000523 if (edgeIndex) {
524 listIndex = abs(edgeIndex) - 1;
525 if (edgeIndex < 0) {
526 fTops[listIndex] = 0;
527 } else {
528 fBottoms[listIndex] = 0;
529 }
530 }
531 if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000532 switch (lastVerb) {
533 case SkPath::kLine_Verb:
534 simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
535 break;
536 case SkPath::kQuad_Verb:
537 simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
538 lastCurve[2].fX, lastCurve[2].fY);
539 break;
540 case SkPath::kCubic_Verb:
541 simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
542 lastCurve[2].fX, lastCurve[2].fY,
543 lastCurve[3].fX, lastCurve[3].fY);
544 break;
545 }
546#if DEBUG_ASSEMBLE
547 SkDebugf("%*s %d %sTo last (%g, %g)\n", tab, "",
548 lastIndex + 1, kLVerbStr[lastVerb],
549 lastCurve[lastVerb].fX, lastCurve[lastVerb].fY);
550#endif
caryclark@google.coma5764232012-03-28 16:20:21 +0000551 if (lastCurve[lastVerb] != firstPt) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000552 simple.lineTo(firstPt.fX, firstPt.fY);
553#if DEBUG_ASSEMBLE
554 SkDebugf("%*s %d final line (%g, %g)\n", tab, "",
555 firstIndex + 1, firstPt.fX, firstPt.fY);
556#endif
caryclark@google.com4917f172012-03-05 22:01:21 +0000557 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000558 simple.close();
caryclark@google.comfb173422012-04-10 18:28:55 +0000559#if DEBUG_ASSEMBLE
560 SkDebugf("%*s close\n", tab, "");
561#endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000562 break;
563 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000564 // if this and next edge go different directions
565#if DEBUG_ASSEMBLE
566 SkDebugf("%*s advance=%d edgeIndex=%d flip=%s\n", tab, "",
567 advance, edgeIndex, advance > 0 ^ edgeIndex < 0 ?
568 "true" : "false");
569#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +0000570 if (advance > 0 ^ edgeIndex < 0) {
571 advance = -advance;
572 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000573 } while (edgeIndex);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000574 } while (true);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000575 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000576
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000577 // sort points by y, then x
578 // if x/y is identical, sort bottoms before tops
579 // if identical and both tops/bottoms, sort by angle
580 static bool lessThan(SkTArray<OutEdge>& edges, const int one,
581 const int two) {
582 const OutEdge& oneEdge = edges[abs(one) - 1];
583 int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
584 const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
585 const OutEdge& twoEdge = edges[abs(two) - 1];
586 int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
587 const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
588 if (startPt1.fY != startPt2.fY) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000589 #if DEBUG_OUT_LESS_THAN
590 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
591 one, two, startPt1.fY, startPt2.fY,
592 startPt1.fY < startPt2.fY ? "true" : "false");
593 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000594 return startPt1.fY < startPt2.fY;
595 }
596 if (startPt1.fX != startPt2.fX) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000597 #if DEBUG_OUT_LESS_THAN
598 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
599 one, two, startPt1.fX, startPt2.fX,
600 startPt1.fX < startPt2.fX ? "true" : "false");
601 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000602 return startPt1.fX < startPt2.fX;
603 }
604 const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
605 const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
606 SkScalar dy1 = startPt1.fY - endPt1.fY;
607 SkScalar dy2 = startPt2.fY - endPt2.fY;
608 SkScalar dy1y2 = dy1 * dy2;
609 if (dy1y2 < 0) { // different signs
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000610 #if DEBUG_OUT_LESS_THAN
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000611 SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
612 dy1 > 0 ? "true" : "false");
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000613 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000614 return dy1 > 0; // one < two if one goes up and two goes down
615 }
616 if (dy1y2 == 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000617 #if DEBUG_OUT_LESS_THAN
618 SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
619 one, two, endPt1.fX < endPt2.fX ? "true" : "false");
620 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000621 return endPt1.fX < endPt2.fX;
622 }
623 SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
624 SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000625 #if DEBUG_OUT_LESS_THAN
626 SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
627 one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
628 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000629 return dy2 > 0 ^ dx1y2 < dx2y1;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000630 }
631
caryclark@google.com6008c6562012-02-15 22:01:16 +0000632 // Sort the indices of paired points and then create more indices so
633 // assemble() can find the next edge and connect the top or bottom
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000634 void bridge() {
635 size_t index;
636 size_t count = fEdges.count();
637 if (!count) {
638 return;
639 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000640 SkASSERT(!fFill || count > 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000641 fTops.setCount(count);
642 sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
643 fBottoms.setCount(count);
644 sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000645 SkTDArray<int> order;
646 for (index = 1; index <= count; ++index) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000647 *order.append() = -index;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000648 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000649 for (index = 1; index <= count; ++index) {
650 *order.append() = index;
651 }
652 QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000653 int* lastPtr = order.end() - 1;
654 int* leftPtr = order.begin();
655 while (leftPtr < lastPtr) {
656 int leftIndex = *leftPtr;
657 int leftOutIndex = abs(leftIndex) - 1;
658 const OutEdge& left = fEdges[leftOutIndex];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000659 int* rightPtr = leftPtr + 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000660 int rightIndex = *rightPtr;
661 int rightOutIndex = abs(rightIndex) - 1;
662 const OutEdge& right = fEdges[rightOutIndex];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000663 bool pairUp = fFill;
664 if (!pairUp) {
665 const SkPoint& leftMatch =
666 left.fPts[leftIndex < 0 ? 0 : left.fVerb];
667 const SkPoint& rightMatch =
668 right.fPts[rightIndex < 0 ? 0 : right.fVerb];
669 pairUp = leftMatch == rightMatch;
670 } else {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000671 #if DEBUG_OUT
caryclark@google.comd88e0892012-03-27 13:23:51 +0000672 // FIXME : not happy that error in low bit is allowed
673 // this probably conceals error elsewhere
674 if (UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
675 right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) > 1) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000676 *fMismatches.append() = leftIndex;
677 if (rightPtr == lastPtr) {
678 *fMismatches.append() = rightIndex;
679 }
680 pairUp = false;
681 }
682 #else
caryclark@google.comd88e0892012-03-27 13:23:51 +0000683 SkASSERT(UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
684 right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) <= 10);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000685 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000686 }
687 if (pairUp) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000688 if (leftIndex < 0) {
689 fTops[leftOutIndex] = rightIndex;
690 } else {
691 fBottoms[leftOutIndex] = rightIndex;
692 }
693 if (rightIndex < 0) {
694 fTops[rightOutIndex] = leftIndex;
695 } else {
696 fBottoms[rightOutIndex] = leftIndex;
697 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000698 ++rightPtr;
699 }
700 leftPtr = rightPtr;
701 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000702#if DEBUG_OUT
703 int* mismatch = fMismatches.begin();
704 while (mismatch != fMismatches.end()) {
705 int leftIndex = *mismatch++;
706 int leftOutIndex = abs(leftIndex) - 1;
707 const OutEdge& left = fEdges[leftOutIndex];
708 const SkPoint& leftPt = left.fPts[leftIndex < 0 ? 0 : left.fVerb];
709 SkDebugf("%s left=%d %s (%1.9g,%1.9g)\n",
710 __FUNCTION__, left.fID, leftIndex < 0 ? "top" : "bot",
711 leftPt.fX, leftPt.fY);
712 }
713 SkASSERT(fMismatches.count() == 0);
714#endif
caryclark@google.comfb173422012-04-10 18:28:55 +0000715#if DEBUG_BRIDGE
716 for (index = 0; index < count; ++index) {
717 const OutEdge& edge = fEdges[index];
718 uint8_t verb = edge.fVerb;
719 SkDebugf("%s %d edge=%d %s (%1.9g,%1.9g) (%1.9g,%1.9g)\n",
720 index == 0 ? __FUNCTION__ : " ",
721 index + 1, edge.fID, kLVerbStr[verb], edge.fPts[0].fX,
722 edge.fPts[0].fY, edge.fPts[verb].fX, edge.fPts[verb].fY);
723 }
724 for (index = 0; index < count; ++index) {
725 SkDebugf(" top of % 2d connects to %s of % 2d\n", index + 1,
726 fTops[index] < 0 ? "top " : "bottom", abs(fTops[index]));
727 SkDebugf(" bottom of % 2d connects to %s of % 2d\n", index + 1,
728 fBottoms[index] < 0 ? "top " : "bottom", abs(fBottoms[index]));
729 }
730#endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000731 }
732
caryclark@google.com6008c6562012-02-15 22:01:16 +0000733protected:
caryclark@google.com6680fb12012-02-07 22:10:51 +0000734 SkTArray<OutEdge> fEdges;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000735 SkTDArray<int> fTops;
736 SkTDArray<int> fBottoms;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000737 bool fFill;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000738#if DEBUG_OUT
739 SkTDArray<int> fMismatches;
740#endif
caryclark@google.com6680fb12012-02-07 22:10:51 +0000741};
742
caryclark@google.comc6825902012-02-03 22:07:47 +0000743// Bounds, unlike Rect, does not consider a vertical line to be empty.
744struct Bounds : public SkRect {
745 static bool Intersects(const Bounds& a, const Bounds& b) {
746 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
747 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
748 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000749
caryclark@google.com6008c6562012-02-15 22:01:16 +0000750 bool isEmpty() {
751 return fLeft > fRight || fTop > fBottom
752 || fLeft == fRight && fTop == fBottom
753 || isnan(fLeft) || isnan(fRight)
754 || isnan(fTop) || isnan(fBottom);
755 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000756};
757
caryclark@google.com4917f172012-03-05 22:01:21 +0000758class Intercepts {
759public:
760 Intercepts()
761 : fTopIntercepts(0)
caryclark@google.com198e0542012-03-30 18:47:02 +0000762 , fBottomIntercepts(0)
763 , fExplicit(false) {
764 }
765
766 Intercepts& operator=(const Intercepts& src) {
767 fTs = src.fTs;
768 fTopIntercepts = src.fTopIntercepts;
769 fBottomIntercepts = src.fBottomIntercepts;
770 }
771
772 // OPTIMIZATION: remove this function if it's never called
773 double t(int tIndex) const {
774 if (tIndex == 0) {
775 return 0;
776 }
777 if (tIndex > fTs.count()) {
778 return 1;
779 }
780 return fTs[tIndex - 1];
caryclark@google.com4917f172012-03-05 22:01:21 +0000781 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000782
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000783#if DEBUG_DUMP
caryclark@google.coma5764232012-03-28 16:20:21 +0000784 void dump(const SkPoint* pts, SkPath::Verb verb) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000785 const char className[] = "Intercepts";
786 const int tab = 8;
787 for (int i = 0; i < fTs.count(); ++i) {
788 SkPoint out;
caryclark@google.coma5764232012-03-28 16:20:21 +0000789 switch (verb) {
790 case SkPath::kLine_Verb:
791 LineXYAtT(pts, fTs[i], &out);
792 break;
793 case SkPath::kQuad_Verb:
794 QuadXYAtT(pts, fTs[i], &out);
795 break;
796 case SkPath::kCubic_Verb:
797 CubicXYAtT(pts, fTs[i], &out);
798 break;
799 default:
800 SkASSERT(0);
801 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000802 SkDebugf("%*s.fTs[%d]=%1.9g (%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000803 className, i, fTs[i], out.fX, out.fY);
804 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000805 SkDebugf("%*s.fTopIntercepts=%u\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000806 className, fTopIntercepts);
caryclark@google.com198e0542012-03-30 18:47:02 +0000807 SkDebugf("%*s.fBottomIntercepts=%u\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000808 className, fBottomIntercepts);
caryclark@google.comfb173422012-04-10 18:28:55 +0000809 SkDebugf("%*s.fExplicit=%d\n", tab + sizeof(className),
810 className, fExplicit);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000811 }
812#endif
813
caryclark@google.comc6825902012-02-03 22:07:47 +0000814 SkTDArray<double> fTs;
caryclark@google.com198e0542012-03-30 18:47:02 +0000815 unsigned char fTopIntercepts; // 0=init state 1=1 edge >1=multiple edges
816 unsigned char fBottomIntercepts;
817 bool fExplicit; // if set, suppress 0 and 1
818
caryclark@google.comc6825902012-02-03 22:07:47 +0000819};
820
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000821struct HorizontalEdge {
822 bool operator<(const HorizontalEdge& rh) const {
823 return fY == rh.fY ? fLeft == rh.fLeft ? fRight < rh.fRight
824 : fLeft < rh.fLeft : fY < rh.fY;
825 }
826
827#if DEBUG_DUMP
828 void dump() {
829 const char className[] = "HorizontalEdge";
830 const int tab = 4;
caryclark@google.com752b60e2012-03-22 21:11:17 +0000831 SkDebugf("%*s.fLeft=%1.9g\n", tab + sizeof(className), className, fLeft);
832 SkDebugf("%*s.fRight=%1.9g\n", tab + sizeof(className), className, fRight);
833 SkDebugf("%*s.fY=%1.9g\n", tab + sizeof(className), className, fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000834 }
835#endif
836
837 SkScalar fLeft;
838 SkScalar fRight;
839 SkScalar fY;
840};
841
caryclark@google.com6680fb12012-02-07 22:10:51 +0000842struct InEdge {
843 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000844 return fBounds.fTop == rh.fBounds.fTop
845 ? fBounds.fLeft < rh.fBounds.fLeft
846 : fBounds.fTop < rh.fBounds.fTop;
847 }
848
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000849 // Avoid collapsing t values that are close to the same since
850 // we walk ts to describe consecutive intersections. Since a pair of ts can
851 // be nearly equal, any problems caused by this should be taken care
852 // of later.
853 int add(double* ts, size_t count, ptrdiff_t verbIndex) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000854 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
caryclark@google.com6008c6562012-02-15 22:01:16 +0000855 bool foundIntercept = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000856 int insertedAt = -1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000857 Intercepts& intercepts = fIntercepts[verbIndex];
caryclark@google.comc6825902012-02-03 22:07:47 +0000858 for (size_t index = 0; index < count; ++index) {
859 double t = ts[index];
caryclark@google.com4917f172012-03-05 22:01:21 +0000860 if (t <= 0) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000861 intercepts.fTopIntercepts <<= 1;
caryclark@google.com4917f172012-03-05 22:01:21 +0000862 fContainsIntercepts |= ++intercepts.fTopIntercepts > 1;
863 continue;
864 }
865 if (t >= 1) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000866 intercepts.fBottomIntercepts <<= 1;
caryclark@google.com4917f172012-03-05 22:01:21 +0000867 fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000868 continue;
869 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000870 fIntersected = true;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000871 foundIntercept = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000872 size_t tCount = intercepts.fTs.count();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000873 double delta;
caryclark@google.comc6825902012-02-03 22:07:47 +0000874 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
875 if (t <= intercepts.fTs[idx2]) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000876 // FIXME: ? if (t < intercepts.fTs[idx2]) // failed
877 delta = intercepts.fTs[idx2] - t;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000878 if (delta > 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000879 insertedAt = idx2;
caryclark@google.comc6825902012-02-03 22:07:47 +0000880 *intercepts.fTs.insert(idx2) = t;
caryclark@google.comc6825902012-02-03 22:07:47 +0000881 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000882 goto nextPt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000883 }
884 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000885 if (tCount == 0 || (delta = t - intercepts.fTs[tCount - 1]) > 0) {
886 insertedAt = tCount;
caryclark@google.comc6825902012-02-03 22:07:47 +0000887 *intercepts.fTs.append() = t;
888 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000889 nextPt:
890 ;
caryclark@google.comc6825902012-02-03 22:07:47 +0000891 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000892 fContainsIntercepts |= foundIntercept;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000893 return insertedAt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000894 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000895
caryclark@google.comfb173422012-04-10 18:28:55 +0000896 void addPartial(SkTArray<InEdge>& edges, int ptStart, int ptEnd,
caryclark@google.com198e0542012-03-30 18:47:02 +0000897 int verbStart, int verbEnd) {
898 InEdge* edge = edges.push_back_n(1);
899 int verbCount = verbEnd - verbStart;
900 edge->fIntercepts.push_back_n(verbCount);
901 uint8_t* verbs = &fVerbs[verbStart];
902 for (int ceptIdx = 0; ceptIdx < verbCount; ++ceptIdx) {
903 edge->fIntercepts[ceptIdx] = fIntercepts[verbStart + ceptIdx];
904 }
905 edge->fPts.append(ptEnd - ptStart, &fPts[ptStart]);
906 edge->fVerbs.append(verbCount, &fVerbs[verbStart]);
907 edge->setBounds();
caryclark@google.com198e0542012-03-30 18:47:02 +0000908 edge->fWinding = fWinding;
909 edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
910 }
911
caryclark@google.comfb173422012-04-10 18:28:55 +0000912 void addSplit(SkTArray<InEdge>& edges, SkPoint* pts, uint8_t verb,
913 Intercepts& intercepts, int firstT, int lastT, bool flipped) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000914 InEdge* edge = edges.push_back_n(1);
915 edge->fIntercepts.push_back_n(1);
caryclark@google.comfb173422012-04-10 18:28:55 +0000916 if (firstT == 0) {
917 *edge->fIntercepts[0].fTs.append() = 0;
918 } else {
919 *edge->fIntercepts[0].fTs.append() = intercepts.fTs[firstT - 1];
920 }
921 bool add1 = lastT == intercepts.fTs.count();
922 edge->fIntercepts[0].fTs.append(lastT - firstT, &intercepts.fTs[firstT]);
923 if (add1) {
924 *edge->fIntercepts[0].fTs.append() = 1;
925 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000926 edge->fIntercepts[0].fExplicit = true;
caryclark@google.comfb173422012-04-10 18:28:55 +0000927 edge->fPts.append(verb + 1, pts);
caryclark@google.com198e0542012-03-30 18:47:02 +0000928 edge->fVerbs.append(1, &verb);
caryclark@google.comfb173422012-04-10 18:28:55 +0000929 // FIXME: bounds could be better for partial Ts
930 edge->setSubBounds();
caryclark@google.com198e0542012-03-30 18:47:02 +0000931 edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
932 if (flipped) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000933 edge->flipTs();
934 edge->fWinding = -fWinding;
935 } else {
936 edge->fWinding = fWinding;
caryclark@google.com198e0542012-03-30 18:47:02 +0000937 }
938 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000939
caryclark@google.com6680fb12012-02-07 22:10:51 +0000940 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000941 // FIXME: in the pathological case where there is a ton of edges, binary search?
942 size_t count = fCached.count();
943 for (size_t index = 0; index < count; ++index) {
944 if (edge == fCached[index]) {
945 return true;
946 }
947 if (edge < fCached[index]) {
948 *fCached.insert(index) = edge;
949 return false;
950 }
951 }
952 *fCached.append() = edge;
953 return false;
954 }
955
caryclark@google.comfb173422012-04-10 18:28:55 +0000956 void complete(signed char winding) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000957 setBounds();
958 fIntercepts.push_back_n(fVerbs.count());
959 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
960 flip();
961 }
962 fContainsIntercepts = fIntersected = false;
caryclark@google.com198e0542012-03-30 18:47:02 +0000963 }
964
caryclark@google.comfb173422012-04-10 18:28:55 +0000965 void flip() {
caryclark@google.com198e0542012-03-30 18:47:02 +0000966 size_t index;
967 size_t last = fPts.count() - 1;
968 for (index = 0; index < last; ++index, --last) {
969 SkTSwap<SkPoint>(fPts[index], fPts[last]);
970 }
971 last = fVerbs.count() - 1;
972 for (index = 0; index < last; ++index, --last) {
973 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
974 }
975 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000976
977 void flipTs() {
978 SkASSERT(fIntercepts.count() == 1);
979 Intercepts& intercepts = fIntercepts[0];
980 SkASSERT(intercepts.fExplicit);
981 SkTDArray<double>& ts = intercepts.fTs;
982 size_t index;
983 size_t last = ts.count() - 1;
984 for (index = 0; index < last; ++index, --last) {
985 SkTSwap<double>(ts[index], ts[last]);
986 }
987 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000988
989 void reset() {
990 fCached.reset();
991 fIntercepts.reset();
992 fPts.reset();
993 fVerbs.reset();
994 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com198e0542012-03-30 18:47:02 +0000995 fWinding = 0;
996 fContainsIntercepts = false;
997 fIntersected = false;
998 }
999
1000 void setBounds() {
caryclark@google.comc6825902012-02-03 22:07:47 +00001001 SkPoint* ptPtr = fPts.begin();
1002 SkPoint* ptLast = fPts.end();
1003 if (ptPtr == ptLast) {
caryclark@google.com198e0542012-03-30 18:47:02 +00001004 SkDebugf("%s empty edge\n", __FUNCTION__);
caryclark@google.comc6825902012-02-03 22:07:47 +00001005 SkASSERT(0);
1006 // FIXME: delete empty edge?
1007 return;
1008 }
1009 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
1010 ++ptPtr;
1011 while (ptPtr != ptLast) {
1012 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
1013 ++ptPtr;
1014 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001015 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001016
1017 // recompute bounds based on subrange of T values
1018 void setSubBounds() {
1019 SkASSERT(fIntercepts.count() == 1);
1020 Intercepts& intercepts = fIntercepts[0];
1021 SkASSERT(intercepts.fExplicit);
1022 SkASSERT(fVerbs.count() == 1);
1023 SkTDArray<double>& ts = intercepts.fTs;
1024 if (fVerbs[0] == SkPath::kQuad_Verb) {
1025 SkASSERT(fPts.count() == 3);
1026 QuadSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
1027 } else {
1028 SkASSERT(fVerbs[0] == SkPath::kCubic_Verb);
1029 SkASSERT(fPts.count() == 4);
1030 CubicSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
1031 }
1032 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001033
caryclark@google.comfb173422012-04-10 18:28:55 +00001034 void splitInflectionPts(SkTArray<InEdge>& edges) {
caryclark@google.com198e0542012-03-30 18:47:02 +00001035 if (!fIntersected) {
1036 return;
1037 }
1038 uint8_t* verbs = fVerbs.begin();
1039 SkPoint* pts = fPts.begin();
1040 int lastVerb = 0;
1041 int lastPt = 0;
1042 uint8_t verb;
1043 bool edgeSplit = false;
1044 for (int ceptIdx = 0; ceptIdx < fIntercepts.count(); ++ceptIdx, pts += verb) {
1045 Intercepts& intercepts = fIntercepts[ceptIdx];
1046 verb = *verbs++;
1047 if (verb <= SkPath::kLine_Verb) {
1048 continue;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001049 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001050 size_t tCount = intercepts.fTs.count();
1051 if (!tCount) {
1052 continue;
1053 }
1054 size_t tIndex = -1;
1055 SkScalar y = pts[0].fY;
1056 int lastSplit = 0;
1057 int firstSplit = -1;
1058 bool curveSplit = false;
1059 while (++tIndex < tCount) {
1060 double nextT = intercepts.fTs[tIndex];
1061 SkScalar nextY = verb == SkPath::kQuad_Verb
1062 ? QuadYAtT(pts, nextT) : CubicYAtT(pts, nextT);
1063 if (nextY < y) {
1064 edgeSplit = curveSplit = true;
1065 if (firstSplit < 0) {
1066 firstSplit = tIndex;
1067 int nextPt = pts - fPts.begin();
1068 int nextVerb = verbs - 1 - fVerbs.begin();
1069 if (lastVerb < nextVerb) {
caryclark@google.comfb173422012-04-10 18:28:55 +00001070 addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
caryclark@google.com198e0542012-03-30 18:47:02 +00001071 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001072 SkDebugf("%s addPartial 1\n", __FUNCTION__);
caryclark@google.com198e0542012-03-30 18:47:02 +00001073 #endif
1074 }
1075 lastPt = nextPt;
1076 lastVerb = nextVerb;
1077 }
1078 } else {
1079 if (firstSplit >= 0) {
1080 if (lastSplit < firstSplit) {
caryclark@google.comfb173422012-04-10 18:28:55 +00001081 addSplit(edges, pts, verb, intercepts,
1082 lastSplit, firstSplit, false);
caryclark@google.com198e0542012-03-30 18:47:02 +00001083 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001084 SkDebugf("%s addSplit 1 tIndex=%d,%d\n",
1085 __FUNCTION__, lastSplit, firstSplit);
caryclark@google.com198e0542012-03-30 18:47:02 +00001086 #endif
1087 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001088 addSplit(edges, pts, verb, intercepts,
1089 firstSplit, tIndex, true);
caryclark@google.com198e0542012-03-30 18:47:02 +00001090 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001091 SkDebugf("%s addSplit 2 tIndex=%d,%d flip\n",
1092 __FUNCTION__, firstSplit, tIndex);
caryclark@google.com198e0542012-03-30 18:47:02 +00001093 #endif
1094 lastSplit = tIndex;
1095 firstSplit = -1;
1096 }
1097 }
1098 y = nextY;
1099 }
1100 if (curveSplit) {
1101 if (firstSplit < 0) {
1102 firstSplit = lastSplit;
1103 } else {
caryclark@google.comfb173422012-04-10 18:28:55 +00001104 addSplit(edges, pts, verb, intercepts, lastSplit,
1105 firstSplit, false);
caryclark@google.com198e0542012-03-30 18:47:02 +00001106 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001107 SkDebugf("%s addSplit 3 tIndex=%d,%d\n", __FUNCTION__,
1108 lastSplit, firstSplit);
caryclark@google.com198e0542012-03-30 18:47:02 +00001109 #endif
1110 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001111 addSplit(edges, pts, verb, intercepts, firstSplit,
1112 tIndex, pts[verb].fY < y);
caryclark@google.com198e0542012-03-30 18:47:02 +00001113 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001114 SkDebugf("%s addSplit 4 tIndex=%d,%d %s\n", __FUNCTION__,
1115 firstSplit, tIndex, pts[verb].fY < y ? "flip" : "");
caryclark@google.com198e0542012-03-30 18:47:02 +00001116 #endif
caryclark@google.com6680fb12012-02-07 22:10:51 +00001117 }
1118 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001119 // collapse remainder -- if there's nothing left, clear it somehow?
1120 if (edgeSplit) {
1121 int nextVerb = verbs - 1 - fVerbs.begin();
1122 if (lastVerb < nextVerb) {
1123 int nextPt = pts - fPts.begin();
caryclark@google.comfb173422012-04-10 18:28:55 +00001124 addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
caryclark@google.com198e0542012-03-30 18:47:02 +00001125 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001126 SkDebugf("%s addPartial 2\n", __FUNCTION__);
caryclark@google.com198e0542012-03-30 18:47:02 +00001127 #endif
1128 }
1129 // OPTIMIZATION: reuse the edge instead of marking it empty
1130 reset();
1131 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001132 }
1133
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001134#if DEBUG_DUMP
1135 void dump() {
1136 int i;
1137 const char className[] = "InEdge";
1138 const int tab = 4;
1139 SkDebugf("InEdge %p (edge=%d)\n", this, fID);
1140 for (i = 0; i < fCached.count(); ++i) {
1141 SkDebugf("%*s.fCached[%d]=0x%08x\n", tab + sizeof(className),
1142 className, i, fCached[i]);
1143 }
1144 uint8_t* verbs = fVerbs.begin();
1145 SkPoint* pts = fPts.begin();
1146 for (i = 0; i < fIntercepts.count(); ++i) {
1147 SkDebugf("%*s.fIntercepts[%d]:\n", tab + sizeof(className),
1148 className, i);
caryclark@google.coma5764232012-03-28 16:20:21 +00001149 fIntercepts[i].dump(pts, (SkPath::Verb) *verbs);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001150 pts += *verbs++;
1151 }
1152 for (i = 0; i < fPts.count(); ++i) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001153 SkDebugf("%*s.fPts[%d]=(%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001154 className, i, fPts[i].fX, fPts[i].fY);
1155 }
1156 for (i = 0; i < fVerbs.count(); ++i) {
1157 SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className),
1158 className, i, fVerbs[i]);
1159 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001160 SkDebugf("%*s.fBounds=(%1.9g, %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001161 className, fBounds.fLeft, fBounds.fTop,
1162 fBounds.fRight, fBounds.fBottom);
1163 SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className,
1164 fWinding);
1165 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
1166 className, fContainsIntercepts);
caryclark@google.com198e0542012-03-30 18:47:02 +00001167 SkDebugf("%*s.fIntersected=%d\n", tab + sizeof(className),
1168 className, fIntersected);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001169 }
1170#endif
1171
caryclark@google.com198e0542012-03-30 18:47:02 +00001172 // FIXME: temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +00001173 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +00001174 SkTArray<Intercepts> fIntercepts; // one per verb
caryclark@google.com4917f172012-03-05 22:01:21 +00001175
caryclark@google.comc6825902012-02-03 22:07:47 +00001176 // persistent data
1177 SkTDArray<SkPoint> fPts;
1178 SkTDArray<uint8_t> fVerbs;
1179 Bounds fBounds;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001180 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +00001181 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001182 bool fContainsIntercepts;
caryclark@google.com198e0542012-03-30 18:47:02 +00001183 bool fIntersected;
caryclark@google.comc6825902012-02-03 22:07:47 +00001184};
1185
caryclark@google.com6680fb12012-02-07 22:10:51 +00001186class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +00001187public:
1188
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001189InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges,
1190 SkTDArray<HorizontalEdge>& horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +00001191 : fPath(path)
1192 , fCurrentEdge(NULL)
1193 , fEdges(edges)
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001194 , fHorizontalEdges(horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +00001195 , fIgnoreHorizontal(ignoreHorizontal)
caryclark@google.com198e0542012-03-30 18:47:02 +00001196 , fContainsCurves(false)
caryclark@google.comc6825902012-02-03 22:07:47 +00001197{
1198 walk();
1199}
1200
caryclark@google.com198e0542012-03-30 18:47:02 +00001201bool containsCurves() const {
1202 return fContainsCurves;
1203}
1204
caryclark@google.comc6825902012-02-03 22:07:47 +00001205protected:
1206
1207void addEdge() {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001208 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +00001209 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
1210 fPtOffset = 1;
1211 *fCurrentEdge->fVerbs.append() = fVerb;
1212}
1213
caryclark@google.com6008c6562012-02-15 22:01:16 +00001214bool complete() {
1215 if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
caryclark@google.comfb173422012-04-10 18:28:55 +00001216 fCurrentEdge->complete(fWinding);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001217 fCurrentEdge = NULL;
1218 return true;
1219 }
1220 return false;
1221}
1222
caryclark@google.com78e17132012-04-17 11:40:34 +00001223int direction(SkPath::Verb verb) {
1224 fPtCount = verb + 1;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001225 if (fIgnoreHorizontal && isHorizontal()) {
caryclark@google.comc6825902012-02-03 22:07:47 +00001226 return 0;
1227 }
caryclark@google.com78e17132012-04-17 11:40:34 +00001228 return fPts[0].fY == fPts[verb].fY
1229 ? fPts[0].fX == fPts[verb].fX ? 0 : fPts[0].fX < fPts[verb].fX
1230 ? 1 : -1 : fPts[0].fY < fPts[verb].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +00001231}
1232
1233bool isHorizontal() {
1234 SkScalar y = fPts[0].fY;
1235 for (int i = 1; i < fPtCount; ++i) {
1236 if (fPts[i].fY != y) {
1237 return false;
1238 }
1239 }
1240 return true;
1241}
1242
1243void startEdge() {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001244 if (!fCurrentEdge) {
1245 fCurrentEdge = fEdges.push_back_n(1);
1246 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001247 fWinding = 0;
1248 fPtOffset = 0;
1249}
1250
1251void walk() {
1252 SkPath::Iter iter(fPath, true);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001253 int winding = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +00001254 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
1255 switch (fVerb) {
1256 case SkPath::kMove_Verb:
caryclark@google.comc6825902012-02-03 22:07:47 +00001257 startEdge();
1258 continue;
1259 case SkPath::kLine_Verb:
caryclark@google.com78e17132012-04-17 11:40:34 +00001260 winding = direction(SkPath::kLine_Verb);
caryclark@google.comc6825902012-02-03 22:07:47 +00001261 break;
1262 case SkPath::kQuad_Verb:
caryclark@google.com78e17132012-04-17 11:40:34 +00001263 fVerb = QuadReduceOrder(fPts);
1264 winding = direction(fVerb);
1265 fContainsCurves |= fVerb == SkPath::kQuad_Verb;
caryclark@google.comc6825902012-02-03 22:07:47 +00001266 break;
1267 case SkPath::kCubic_Verb:
caryclark@google.com78e17132012-04-17 11:40:34 +00001268 fVerb = CubicReduceOrder(fPts);
1269 winding = direction(fVerb);
1270 fContainsCurves |= fVerb >= SkPath::kQuad_Verb;
caryclark@google.comc6825902012-02-03 22:07:47 +00001271 break;
1272 case SkPath::kClose_Verb:
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001273 SkASSERT(fCurrentEdge);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001274 complete();
caryclark@google.comc6825902012-02-03 22:07:47 +00001275 continue;
1276 default:
1277 SkDEBUGFAIL("bad verb");
1278 return;
1279 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001280 if (winding == 0) {
1281 HorizontalEdge* horizontalEdge = fHorizontalEdges.append();
1282 // FIXME: for degenerate quads and cubics, compute x extremes
1283 horizontalEdge->fLeft = fPts[0].fX;
1284 horizontalEdge->fRight = fPts[fVerb].fX;
1285 horizontalEdge->fY = fPts[0].fY;
1286 if (horizontalEdge->fLeft > horizontalEdge->fRight) {
1287 SkTSwap<SkScalar>(horizontalEdge->fLeft, horizontalEdge->fRight);
1288 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001289 if (complete()) {
1290 startEdge();
1291 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001292 continue;
1293 }
1294 if (fWinding + winding == 0) {
1295 // FIXME: if prior verb or this verb is a horizontal line, reverse
1296 // it instead of starting a new edge
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001297 SkASSERT(fCurrentEdge);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001298 if (complete()) {
1299 startEdge();
1300 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001301 }
1302 fWinding = winding;
1303 addEdge();
1304 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001305 if (!complete()) {
1306 if (fCurrentEdge) {
1307 fEdges.pop_back();
1308 }
1309 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001310}
1311
1312private:
1313 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001314 InEdge* fCurrentEdge;
1315 SkTArray<InEdge>& fEdges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001316 SkTDArray<HorizontalEdge>& fHorizontalEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +00001317 SkPoint fPts[4];
1318 SkPath::Verb fVerb;
1319 int fPtCount;
1320 int fPtOffset;
1321 int8_t fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +00001322 bool fIgnoreHorizontal;
caryclark@google.com198e0542012-03-30 18:47:02 +00001323 bool fContainsCurves;
caryclark@google.comc6825902012-02-03 22:07:47 +00001324};
1325
caryclark@google.com6680fb12012-02-07 22:10:51 +00001326struct WorkEdge {
1327 SkScalar bottom() const {
1328 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +00001329 }
1330
caryclark@google.com6680fb12012-02-07 22:10:51 +00001331 void init(const InEdge* edge) {
1332 fEdge = edge;
1333 fPts = edge->fPts.begin();
1334 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00001335 }
1336
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001337 bool advance() {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001338 SkASSERT(fVerb < fEdge->fVerbs.end());
1339 fPts += *fVerb++;
1340 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +00001341 }
caryclark@google.com78e17132012-04-17 11:40:34 +00001342
1343 const SkPoint* lastPoints() const {
1344 SkASSERT(fPts >= fEdge->fPts.begin() + lastVerb());
1345 return &fPts[-lastVerb()];
1346 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001347
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001348 SkPath::Verb lastVerb() const {
1349 SkASSERT(fVerb > fEdge->fVerbs.begin());
1350 return (SkPath::Verb) fVerb[-1];
1351 }
1352
caryclark@google.com78e17132012-04-17 11:40:34 +00001353 const SkPoint* points() const {
1354 return fPts;
1355 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001356
1357 SkPath::Verb verb() const {
1358 return (SkPath::Verb) *fVerb;
1359 }
1360
caryclark@google.com6008c6562012-02-15 22:01:16 +00001361 ptrdiff_t verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001362 return fVerb - fEdge->fVerbs.begin();
1363 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001364
caryclark@google.com6680fb12012-02-07 22:10:51 +00001365 int winding() const {
1366 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +00001367 }
1368
caryclark@google.com6680fb12012-02-07 22:10:51 +00001369 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +00001370 const SkPoint* fPts;
1371 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +00001372};
1373
caryclark@google.com6680fb12012-02-07 22:10:51 +00001374// always constructed with SkTDArray because new edges are inserted
1375// this may be a inappropriate optimization, suggesting that a separate array of
1376// ActiveEdge* may be faster to insert and search
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001377
1378// OPTIMIZATION: Brian suggests that global sorting should be unnecessary, since
1379// as active edges are introduced, only local sorting should be required
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001380class ActiveEdge {
1381public:
caryclark@google.com78e17132012-04-17 11:40:34 +00001382 // this logic must be kept in sync with tooCloseToCall
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001383 // callers expect this to only read fAbove, fTangent
caryclark@google.com6008c6562012-02-15 22:01:16 +00001384 bool operator<(const ActiveEdge& rh) const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001385 if (fVerb == rh.fVerb) {
1386 // FIXME: don't know what to do if verb is quad, cubic
1387 return abCompare(fAbove, fBelow, rh.fAbove, rh.fBelow);
caryclark@google.comd88e0892012-03-27 13:23:51 +00001388 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001389 // figure out which is quad, line
1390 // if cached data says line did not intersect quad, use top/bottom
1391 if (fVerb != SkPath::kLine_Verb ? noIntersect(rh) : rh.noIntersect(*this)) {
1392 return abCompare(fAbove, fBelow, rh.fAbove, rh.fBelow);
1393 }
1394 // use whichever of top/tangent tangent/bottom overlaps more
1395 // with line top/bot
1396 // assumes quad/cubic can already be upconverted to cubic/cubic
1397 const SkPoint* line[2];
1398 const SkPoint* curve[4];
1399 if (fVerb != SkPath::kLine_Verb) {
1400 line[0] = &rh.fAbove;
1401 line[1] = &rh.fBelow;
1402 curve[0] = &fAbove;
1403 curve[1] = &fTangent;
1404 curve[2] = &fBelow;
1405 } else {
1406 line[0] = &fAbove;
1407 line[1] = &fBelow;
1408 curve[0] = &rh.fAbove;
1409 curve[1] = &rh.fTangent;
1410 curve[2] = &rh.fBelow;
1411 }
1412
1413 }
1414
1415 bool abCompare(const SkPoint& a1, const SkPoint& a2, const SkPoint& b1,
1416 const SkPoint& b2) const {
1417 double topD = a1.fX - b1.fX;
1418 if (b1.fY < a1.fY) {
1419 topD = (b2.fY - b1.fY) * topD - (a1.fY - b1.fY) * (b2.fX - b1.fX);
1420 } else if (b1.fY > a1.fY) {
1421 topD = (a2.fY - a1.fY) * topD + (b1.fY - a1.fY) * (a2.fX - a1.fX);
1422 }
1423 double botD = a2.fX - b2.fX;
1424 if (b2.fY > a2.fY) {
1425 botD = (b2.fY - b1.fY) * botD - (a2.fY - b2.fY) * (b2.fX - b1.fX);
1426 } else if (b2.fY < a2.fY) {
1427 botD = (a2.fY - a1.fY) * botD + (b2.fY - a2.fY) * (a2.fX - a1.fX);
caryclark@google.comd88e0892012-03-27 13:23:51 +00001428 }
1429 // return sign of greater absolute value
1430 return (fabs(topD) > fabs(botD) ? topD : botD) < 0;
1431 }
1432
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001433 // If a pair of edges are nearly coincident for some span, add a T in the
1434 // edge so it can be shortened to match the other edge. Note that another
1435 // approach is to trim the edge after it is added to the OutBuilder list --
1436 // FIXME: since this has no effect if the edge is already done (i.e.,
1437 // fYBottom >= y) maybe this can only be done by calling trimLine later.
1438 void addTatYBelow(SkScalar y) {
1439 if (fBelow.fY <= y || fYBottom >= y) {
1440 return;
1441 }
1442 addTatYInner(y);
1443 fFixBelow = true;
1444 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001445
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001446 void addTatYAbove(SkScalar y) {
1447 if (fBelow.fY <= y) {
1448 return;
1449 }
1450 addTatYInner(y);
1451 }
1452
1453 void addTatYInner(SkScalar y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001454 if (fWorkEdge.fPts[0].fY > y) {
1455 backup(y);
1456 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001457 SkScalar left = fWorkEdge.fPts[0].fX;
1458 SkScalar right = fWorkEdge.fPts[1].fX;
1459 if (left > right) {
1460 SkTSwap(left, right);
1461 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001462 double ts[2];
caryclark@google.com78e17132012-04-17 11:40:34 +00001463 SkASSERT(fWorkEdge.fVerb[0] == SkPath::kLine_Verb);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001464 int pts = LineIntersect(fWorkEdge.fPts, left, right, y, ts);
1465 SkASSERT(pts == 1);
1466 // An ActiveEdge or WorkEdge has no need to modify the T values computed
1467 // in the InEdge, except in the following case. If a pair of edges are
1468 // nearly coincident, this may not be detected when the edges are
1469 // intersected. Later, when sorted, and this near-coincidence is found,
1470 // an additional t value must be added, requiring the cast below.
1471 InEdge* writable = const_cast<InEdge*>(fWorkEdge.fEdge);
1472 int insertedAt = writable->add(ts, pts, fWorkEdge.verbIndex());
caryclark@google.com752b60e2012-03-22 21:11:17 +00001473 #if DEBUG_ADJUST_COINCIDENT
1474 SkDebugf("%s edge=%d y=%1.9g t=%1.9g\n", __FUNCTION__, ID(), y, ts[0]);
1475 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001476 if (insertedAt >= 0) {
1477 if (insertedAt + 1 < fTIndex) {
1478 SkASSERT(insertedAt + 2 == fTIndex);
1479 --fTIndex;
1480 }
1481 }
1482 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001483
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001484 bool advanceT() {
caryclark@google.com198e0542012-03-30 18:47:02 +00001485 SkASSERT(fTIndex <= fTs->count() - fExplicitTs);
1486 return ++fTIndex <= fTs->count() - fExplicitTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001487 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001488
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001489 bool advance() {
1490 // FIXME: flip sense of next
1491 bool result = fWorkEdge.advance();
1492 fDone = !result;
1493 initT();
1494 return result;
1495 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001496
1497 void backup(SkScalar y) {
1498 do {
1499 SkASSERT(fWorkEdge.fEdge->fVerbs.begin() < fWorkEdge.fVerb);
1500 fWorkEdge.fPts -= *--fWorkEdge.fVerb;
1501 SkASSERT(fWorkEdge.fEdge->fPts.begin() <= fWorkEdge.fPts);
1502 } while (fWorkEdge.fPts[0].fY >= y);
1503 initT();
caryclark@google.com198e0542012-03-30 18:47:02 +00001504 SkASSERT(!fExplicitTs);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001505 fTIndex = fTs->count() + 1;
1506 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001507
1508 void calcAboveBelow(double tAbove, double tBelow) {
1509 fVerb = fWorkEdge.verb();
1510 switch (fVerb) {
1511 case SkPath::kLine_Verb:
1512 LineXYAtT(fWorkEdge.fPts, tAbove, &fAbove);
1513 LineXYAtT(fWorkEdge.fPts, tBelow, &fTangent);
1514 fBelow = fTangent;
1515 break;
1516 case SkPath::kQuad_Verb:
1517 // FIXME: put array in struct to avoid copy?
1518 SkPoint quad[3];
1519 QuadSubDivide(fWorkEdge.fPts, tAbove, tBelow, quad);
1520 fAbove = quad[0];
1521 fTangent = quad[0] != quad[1] ? quad[1] : quad[2];
1522 fBelow = quad[2];
1523 break;
1524 case SkPath::kCubic_Verb:
1525 SkPoint cubic[3];
1526 CubicSubDivide(fWorkEdge.fPts, tAbove, tBelow, cubic);
1527 fAbove = cubic[0];
1528 // FIXME: can't see how quad logic for how tangent is used
1529 // extends to cubic
1530 fTangent = cubic[0] != cubic[1] ? cubic[1]
1531 : cubic[0] != cubic[2] ? cubic[2] : cubic[3];
1532 fBelow = cubic[3];
1533 break;
1534 default:
1535 SkASSERT(0);
1536 }
1537 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001538
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001539 void calcLeft(SkScalar y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001540 // OPTIMIZE: put a kDone_Verb at the end of the verb list?
caryclark@google.com4917f172012-03-05 22:01:21 +00001541 if (fDone || fBelow.fY > y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001542 return; // nothing to do; use last
caryclark@google.com4917f172012-03-05 22:01:21 +00001543 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001544 calcLeft();
caryclark@google.com752b60e2012-03-22 21:11:17 +00001545 if (fAbove.fY == fBelow.fY) {
1546 SkDebugf("%s edge=%d fAbove.fY != fBelow.fY %1.9g\n", __FUNCTION__,
1547 ID(), fAbove.fY);
1548 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001549 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001550
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001551 void calcLeft() {
caryclark@google.com198e0542012-03-30 18:47:02 +00001552 int add = (fTIndex <= fTs->count() - fExplicitTs) - 1;
caryclark@google.coma5764232012-03-28 16:20:21 +00001553 double tAbove = t(fTIndex + add);
caryclark@google.coma5764232012-03-28 16:20:21 +00001554 double tBelow = t(fTIndex - ~add);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001555 // OPTIMIZATION: if fAbove, fBelow have already been computed
1556 // for the fTIndex, don't do it again
1557 calcAboveBelow(tAbove, tBelow);
1558 // For identical x, this lets us know which edge is first.
1559 // If both edges have T values < 1, check x at next T (fBelow).
caryclark@google.coma5764232012-03-28 16:20:21 +00001560 SkASSERT(tAbove != tBelow);
1561 // FIXME: this can loop forever
1562 // need a break if we hit the end
caryclark@google.com198e0542012-03-30 18:47:02 +00001563 // FIXME: in unit test, figure out how explicit Ts work as well
caryclark@google.coma5764232012-03-28 16:20:21 +00001564 while (fAbove.fY == fBelow.fY) {
1565 if (add < 0 || fTIndex == fTs->count()) {
1566 add -= 1;
1567 SkASSERT(fTIndex + add >= 0);
1568 tAbove = t(fTIndex + add);
caryclark@google.coma5764232012-03-28 16:20:21 +00001569 } else {
1570 add += 1;
1571 SkASSERT(fTIndex - ~add <= fTs->count() + 1);
1572 tBelow = t(fTIndex - ~add);
caryclark@google.coma5764232012-03-28 16:20:21 +00001573 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001574 calcAboveBelow(tAbove, tBelow);
caryclark@google.coma5764232012-03-28 16:20:21 +00001575 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001576 fTAbove = tAbove;
1577 fTBelow = tBelow;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001578 }
1579
caryclark@google.com752b60e2012-03-22 21:11:17 +00001580 bool done(SkScalar bottom) const {
1581 return fDone || fYBottom >= bottom;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001582 }
caryclark@google.com78e17132012-04-17 11:40:34 +00001583
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001584 void fixBelow() {
1585 if (fFixBelow) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001586 fTBelow = nextT();
1587 calcAboveBelow(fTAbove, fTBelow);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001588 fFixBelow = false;
1589 }
1590 }
1591
caryclark@google.com6680fb12012-02-07 22:10:51 +00001592 void init(const InEdge* edge) {
1593 fWorkEdge.init(edge);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001594 fDone = false;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001595 initT();
caryclark@google.com4917f172012-03-05 22:01:21 +00001596 fBelow.fY = SK_ScalarMin;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001597 fYBottom = SK_ScalarMin;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001598 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001599
caryclark@google.com6680fb12012-02-07 22:10:51 +00001600 void initT() {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001601 const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
1602 SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
1603 const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
caryclark@google.com198e0542012-03-30 18:47:02 +00001604 fTs = &interceptPtr->fTs;
1605 fExplicitTs = interceptPtr->fExplicit;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001606 // the above is conceptually the same as
1607 // fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
1608 // but templated arrays don't allow returning a pointer to the end() element
caryclark@google.com6680fb12012-02-07 22:10:51 +00001609 fTIndex = 0;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001610 if (!fDone) {
1611 fVerb = fWorkEdge.verb();
1612 }
1613 SkASSERT(fVerb > SkPath::kMove_Verb);
caryclark@google.comc6825902012-02-03 22:07:47 +00001614 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001615
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001616 // OPTIMIZATION: record if two edges are coincident when the are intersected
1617 // It's unclear how to do this -- seems more complicated than recording the
1618 // t values, since the same t values could exist intersecting non-coincident
1619 // edges.
caryclark@google.com78e17132012-04-17 11:40:34 +00001620 bool isCoincidentWith(const ActiveEdge* edge) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001621 if (fAbove != edge->fAbove || fBelow != edge->fBelow) {
1622 return false;
1623 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001624 if (fVerb != edge->fVerb) {
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001625 return false;
1626 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001627 switch (fVerb) {
caryclark@google.com78e17132012-04-17 11:40:34 +00001628 case SkPath::kLine_Verb:
caryclark@google.com4917f172012-03-05 22:01:21 +00001629 return true;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001630 default:
caryclark@google.com78e17132012-04-17 11:40:34 +00001631 // FIXME: add support for quads, cubics
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001632 SkASSERT(0);
caryclark@google.com78e17132012-04-17 11:40:34 +00001633 return false;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001634 }
1635 return false;
1636 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001637
caryclark@google.com78e17132012-04-17 11:40:34 +00001638 bool isUnordered(const ActiveEdge* edge) const {
1639 return fAbove == edge->fAbove && fBelow == edge->fBelow;
1640 }
1641
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001642// SkPath::Verb lastVerb() const {
1643// return fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
1644// }
caryclark@google.com78e17132012-04-17 11:40:34 +00001645
1646 const SkPoint* lastPoints() const {
1647 return fDone ? fWorkEdge.lastPoints() : fWorkEdge.points();
1648 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001649
1650 bool noIntersect(const ActiveEdge& ) const {
1651 // incomplete
1652 return false;
1653 }
caryclark@google.com78e17132012-04-17 11:40:34 +00001654
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001655 // The shortest close call edge should be moved into a position where
1656 // it contributes if the winding is transitioning to or from zero.
1657 bool swapClose(const ActiveEdge* next, int prev, int wind, int mask) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001658#if DEBUG_ADJUST_COINCIDENT
1659 SkDebugf("%s edge=%d (%g) next=%d (%g) prev=%d wind=%d nextWind=%d\n",
1660 __FUNCTION__, ID(), fBelow.fY, next->ID(), next->fBelow.fY,
1661 prev, wind, wind + next->fWorkEdge.winding());
1662#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001663 if ((prev & mask) == 0 || (wind & mask) == 0) {
1664 return next->fBelow.fY < fBelow.fY;
1665 }
1666 int nextWinding = wind + next->fWorkEdge.winding();
1667 if ((nextWinding & mask) == 0) {
1668 return fBelow.fY < next->fBelow.fY;
1669 }
1670 return false;
1671 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001672
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001673 bool swapCoincident(const ActiveEdge* edge, SkScalar bottom) const {
1674 if (fBelow.fY >= bottom || fDone || edge->fDone) {
1675 return false;
1676 }
1677 ActiveEdge thisWork = *this;
1678 ActiveEdge edgeWork = *edge;
1679 while ((thisWork.advanceT() || thisWork.advance())
1680 && (edgeWork.advanceT() || edgeWork.advance())) {
1681 thisWork.calcLeft();
1682 edgeWork.calcLeft();
1683 if (thisWork < edgeWork) {
1684 return false;
1685 }
1686 if (edgeWork < thisWork) {
1687 return true;
1688 }
1689 }
1690 return false;
1691 }
caryclark@google.com78e17132012-04-17 11:40:34 +00001692
1693 bool swapUnordered(const ActiveEdge* edge, SkScalar bottom) const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001694 SkASSERT(fVerb != SkPath::kLine_Verb
1695 || edge->fVerb != SkPath::kLine_Verb);
caryclark@google.com78e17132012-04-17 11:40:34 +00001696 if (fDone || edge->fDone) {
1697 return false;
1698 }
1699 ActiveEdge thisWork, edgeWork;
1700 extractAboveBelow(thisWork);
1701 edge->extractAboveBelow(edgeWork);
1702 return edgeWork < thisWork;
1703 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001704
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001705 bool tooCloseToCall(const ActiveEdge* edge) const {
1706 int ulps;
caryclark@google.comd88e0892012-03-27 13:23:51 +00001707 double t1, t2, b1, b2;
caryclark@google.com78e17132012-04-17 11:40:34 +00001708 // This logic must be kept in sync with operator <
caryclark@google.comd88e0892012-03-27 13:23:51 +00001709 if (edge->fAbove.fY < fAbove.fY) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001710 t1 = (edge->fTangent.fY - edge->fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
1711 t2 = (fAbove.fY - edge->fAbove.fY) * (edge->fTangent.fX - edge->fAbove.fX);
caryclark@google.comd88e0892012-03-27 13:23:51 +00001712 } else if (edge->fAbove.fY > fAbove.fY) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001713 t1 = (fTangent.fY - fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
1714 t2 = (fAbove.fY - edge->fAbove.fY) * (fTangent.fX - fAbove.fX);
caryclark@google.comd88e0892012-03-27 13:23:51 +00001715 } else {
1716 t1 = fAbove.fX;
1717 t2 = edge->fAbove.fX;
1718 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001719 if (edge->fTangent.fY > fTangent.fY) {
1720 b1 = (edge->fTangent.fY - edge->fAbove.fY) * (fTangent.fX - edge->fTangent.fX);
1721 b2 = (fTangent.fY - edge->fTangent.fY) * (edge->fTangent.fX - edge->fAbove.fX);
1722 } else if (edge->fTangent.fY < fTangent.fY) {
1723 b1 = (fTangent.fY - fAbove.fY) * (fTangent.fX - edge->fTangent.fX);
1724 b2 = (fTangent.fY - edge->fTangent.fY) * (fTangent.fX - fAbove.fX);
caryclark@google.comd88e0892012-03-27 13:23:51 +00001725 } else {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001726 b1 = fTangent.fX;
1727 b2 = edge->fTangent.fX;
caryclark@google.comd88e0892012-03-27 13:23:51 +00001728 }
1729 if (fabs(t1 - t2) > fabs(b1 - b2)) {
1730 ulps = UlpsDiff(t1, t2);
1731 } else {
1732 ulps = UlpsDiff(b1, b2);
1733 }
1734#if DEBUG_ADJUST_COINCIDENT
1735 SkDebugf("%s this=%d edge=%d ulps=%d\n", __FUNCTION__, ID(), edge->ID(),
1736 ulps);
1737#endif
caryclark@google.com78e17132012-04-17 11:40:34 +00001738 if (ulps < 0 || ulps > 32) {
1739 return false;
1740 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001741 if (fVerb == SkPath::kLine_Verb && edge->fVerb == SkPath::kLine_Verb) {
caryclark@google.com78e17132012-04-17 11:40:34 +00001742 return true;
1743 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001744 if (fVerb != SkPath::kLine_Verb && edge->fVerb != SkPath::kLine_Verb) {
caryclark@google.com78e17132012-04-17 11:40:34 +00001745 return false;
1746 }
1747
1748 double ts[2];
1749 bool isLine = true;
1750 bool curveQuad = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001751 if (fVerb == SkPath::kCubic_Verb) {
caryclark@google.com78e17132012-04-17 11:40:34 +00001752 ts[0] = (fTAbove * 2 + fTBelow) / 3;
1753 ts[1] = (fTAbove + fTBelow * 2) / 3;
1754 curveQuad = isLine = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001755 } else if (edge->fVerb == SkPath::kCubic_Verb) {
caryclark@google.com78e17132012-04-17 11:40:34 +00001756 ts[0] = (edge->fTAbove * 2 + edge->fTBelow) / 3;
1757 ts[1] = (edge->fTAbove + edge->fTBelow * 2) / 3;
1758 curveQuad = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001759 } else if (fVerb == SkPath::kQuad_Verb) {
caryclark@google.com78e17132012-04-17 11:40:34 +00001760 ts[0] = fTAbove;
1761 ts[1] = (fTAbove + fTBelow) / 2;
1762 isLine = false;
1763 } else {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001764 SkASSERT(edge->fVerb == SkPath::kQuad_Verb);
caryclark@google.com78e17132012-04-17 11:40:34 +00001765 ts[0] = edge->fTAbove;
1766 ts[1] = (edge->fTAbove + edge->fTBelow) / 2;
1767 }
1768 const SkPoint* curvePts = isLine ? edge->lastPoints() : lastPoints();
1769 const ActiveEdge* lineEdge = isLine ? this : edge;
1770 SkPoint curveSample[2];
1771 for (int index = 0; index < 2; ++index) {
1772 if (curveQuad) {
1773 QuadXYAtT(curvePts, ts[index], &curveSample[index]);
1774 } else {
1775 CubicXYAtT(curvePts, ts[index], &curveSample[index]);
1776 }
1777 }
1778 return IsCoincident(curveSample, lineEdge->fAbove, lineEdge->fBelow);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001779 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001780
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001781 double nextT() const {
caryclark@google.com198e0542012-03-30 18:47:02 +00001782 SkASSERT(fTIndex <= fTs->count() - fExplicitTs);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001783 return t(fTIndex + 1);
1784 }
caryclark@google.com6680fb12012-02-07 22:10:51 +00001785
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001786 double t() const {
caryclark@google.com198e0542012-03-30 18:47:02 +00001787 return t(fTIndex);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001788 }
1789
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001790 double t(int tIndex) const {
caryclark@google.com198e0542012-03-30 18:47:02 +00001791 if (fExplicitTs) {
1792 SkASSERT(tIndex < fTs->count());
1793 return (*fTs)[tIndex];
1794 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001795 if (tIndex == 0) {
1796 return 0;
1797 }
1798 if (tIndex > fTs->count()) {
1799 return 1;
1800 }
1801 return (*fTs)[tIndex - 1];
1802 }
1803
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001804 // FIXME: debugging only
caryclark@google.com752b60e2012-03-22 21:11:17 +00001805 int ID() const {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001806 return fWorkEdge.fEdge->fID;
1807 }
1808
caryclark@google.com78e17132012-04-17 11:40:34 +00001809private:
1810 // utility used only by swapUnordered
1811 void extractAboveBelow(ActiveEdge& extracted) const {
1812 SkPoint curve[4];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001813 switch (fVerb) {
caryclark@google.com78e17132012-04-17 11:40:34 +00001814 case SkPath::kLine_Verb:
1815 extracted.fAbove = fAbove;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001816 extracted.fTangent = fTangent;
caryclark@google.com78e17132012-04-17 11:40:34 +00001817 return;
1818 case SkPath::kQuad_Verb:
1819 QuadSubDivide(lastPoints(), fTAbove, fTBelow, curve);
1820 break;
1821 case SkPath::kCubic_Verb:
1822 CubicSubDivide(lastPoints(), fTAbove, fTBelow, curve);
1823 break;
1824 default:
1825 SkASSERT(0);
1826 }
1827 extracted.fAbove = curve[0];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001828 extracted.fTangent = curve[1];
caryclark@google.com78e17132012-04-17 11:40:34 +00001829 }
1830
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001831public:
caryclark@google.com6680fb12012-02-07 22:10:51 +00001832 WorkEdge fWorkEdge;
1833 const SkTDArray<double>* fTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001834 SkPoint fAbove;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001835 SkPoint fTangent;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001836 SkPoint fBelow;
caryclark@google.com78e17132012-04-17 11:40:34 +00001837 double fTAbove; // OPTIMIZATION: only required if edge has quads or cubics
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001838 double fTBelow;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001839 SkScalar fYBottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001840 int fCoincident;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001841 int fTIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001842 SkPath::Verb fVerb;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001843 bool fSkip; // OPTIMIZATION: use bitfields?
1844 bool fCloseCall;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001845 bool fDone;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001846 bool fFixBelow;
caryclark@google.com198e0542012-03-30 18:47:02 +00001847 bool fExplicitTs;
caryclark@google.comc6825902012-02-03 22:07:47 +00001848};
1849
caryclark@google.com6680fb12012-02-07 22:10:51 +00001850static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001851 size_t count = activeEdges.count();
1852 for (size_t index = 0; index < count; ++index) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001853 if (edge == activeEdges[index].fWorkEdge.fEdge) {
1854 return;
1855 }
1856 }
1857 ActiveEdge* active = activeEdges.append();
1858 active->init(edge);
1859}
1860
caryclark@google.com4917f172012-03-05 22:01:21 +00001861// Find any intersections in the range of active edges. A pair of edges, on
1862// either side of another edge, may change the winding contribution for part of
1863// the edge.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001864// Keep horizontal edges just for
caryclark@google.com4917f172012-03-05 22:01:21 +00001865// the purpose of computing when edges change their winding contribution, since
1866// this is essentially computing the horizontal intersection.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001867static void addBottomT(InEdge** currentPtr, InEdge** lastPtr,
1868 HorizontalEdge** horizontal) {
1869 InEdge** testPtr = currentPtr - 1;
1870 HorizontalEdge* horzEdge = *horizontal;
1871 SkScalar left = horzEdge->fLeft;
1872 SkScalar bottom = horzEdge->fY;
1873 while (++testPtr != lastPtr) {
1874 InEdge* test = *testPtr;
1875 if (test->fBounds.fBottom <= bottom || test->fBounds.fRight <= left) {
1876 continue;
1877 }
1878 WorkEdge wt;
1879 wt.init(test);
1880 do {
1881 HorizontalEdge** sorted = horizontal;
1882 horzEdge = *sorted;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001883 do {
caryclark@google.com198e0542012-03-30 18:47:02 +00001884 double wtTs[4];
1885 int pts;
1886 uint8_t verb = wt.verb();
1887 switch (verb) {
1888 case SkPath::kLine_Verb:
1889 pts = LineIntersect(wt.fPts, horzEdge->fLeft,
1890 horzEdge->fRight, horzEdge->fY, wtTs);
1891 break;
1892 case SkPath::kQuad_Verb:
1893 pts = QuadIntersect(wt.fPts, horzEdge->fLeft,
1894 horzEdge->fRight, horzEdge->fY, wtTs);
1895 break;
1896 case SkPath::kCubic_Verb:
1897 pts = CubicIntersect(wt.fPts, horzEdge->fLeft,
1898 horzEdge->fRight, horzEdge->fY, wtTs);
1899 break;
1900 }
1901 if (pts) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001902#if DEBUG_ADD_BOTTOM_TS
caryclark@google.com198e0542012-03-30 18:47:02 +00001903 for (int x = 0; x < pts; ++x) {
1904 SkDebugf("%s y=%g wtTs[0]=%g (%g,%g", __FUNCTION__,
1905 horzEdge->fY, wtTs[x], wt.fPts[0].fX, wt.fPts[0].fY);
1906 for (int y = 0; y < verb; ++y) {
1907 SkDebugf(" %g,%g", wt.fPts[y + 1].fX, wt.fPts[y + 1].fY));
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001908 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001909 SkDebugf(")\n");
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001910 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001911 if (pts > verb) {
1912 SkASSERT(0); // FIXME ? should this work?
1913 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1914 }
1915#endif
1916 test->add(wtTs, pts, wt.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001917 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001918 horzEdge = *++sorted;
1919 } while (horzEdge->fY == bottom
1920 && horzEdge->fLeft <= test->fBounds.fRight);
1921 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001922 }
1923}
1924
caryclark@google.coma5764232012-03-28 16:20:21 +00001925static void debugShowLineIntersection(int pts, const WorkEdge& wt,
1926 const WorkEdge& wn, const double wtTs[2], const double wnTs[2]) {
1927#if DEBUG_ADD_INTERSECTING_TS
1928 if (!pts) {
1929 return;
1930 }
1931 SkPoint wtOutPt, wnOutPt;
1932 LineXYAtT(wt.fPts, wtTs[0], &wtOutPt);
1933 LineXYAtT(wn.fPts, wnTs[0], &wnOutPt);
caryclark@google.comfb173422012-04-10 18:28:55 +00001934 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
caryclark@google.coma5764232012-03-28 16:20:21 +00001935 __FUNCTION__,
1936 wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
caryclark@google.comfb173422012-04-10 18:28:55 +00001937 wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY);
caryclark@google.coma5764232012-03-28 16:20:21 +00001938 if (pts == 2) {
1939 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1940 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001941 SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
caryclark@google.coma5764232012-03-28 16:20:21 +00001942 __FUNCTION__,
1943 wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY,
caryclark@google.comfb173422012-04-10 18:28:55 +00001944 wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY);
caryclark@google.coma5764232012-03-28 16:20:21 +00001945 if (pts == 2) {
1946 SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
1947 }
1948#endif
1949}
1950
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001951static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001952 InEdge** testPtr = currentPtr - 1;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001953 // FIXME: lastPtr should be past the point of interest, so
1954 // test below should be lastPtr - 2
1955 // that breaks testSimplifyTriangle22, so further investigation is needed
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001956 while (++testPtr != lastPtr - 1) {
1957 InEdge* test = *testPtr;
1958 InEdge** nextPtr = testPtr;
1959 do {
1960 InEdge* next = *++nextPtr;
caryclark@google.com198e0542012-03-30 18:47:02 +00001961 // FIXME: this compares against the sentinel sometimes
1962 // OPTIMIZATION: this may never be needed since this gets called
1963 // in two passes now. Verify that double hits are appropriate.
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001964 if (test->cached(next)) {
1965 continue;
1966 }
1967 if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
1968 continue;
1969 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001970 WorkEdge wt, wn;
1971 wt.init(test);
1972 wn.init(next);
1973 do {
caryclark@google.coma5764232012-03-28 16:20:21 +00001974 int pts;
1975 Intersections ts;
1976 bool swap = false;
1977 switch (wt.verb()) {
1978 case SkPath::kLine_Verb:
1979 switch (wn.verb()) {
1980 case SkPath::kLine_Verb: {
1981 pts = LineIntersect(wt.fPts, wn.fPts, ts);
1982 debugShowLineIntersection(pts, wt, wn,
1983 ts.fT[0], ts.fT[1]);
1984 break;
1985 }
1986 case SkPath::kQuad_Verb: {
1987 swap = true;
1988 pts = QuadLineIntersect(wn.fPts, wt.fPts, ts);
1989 break;
1990 }
1991 case SkPath::kCubic_Verb: {
1992 swap = true;
1993 pts = CubicLineIntersect(wn.fPts, wt.fPts, ts);
1994 break;
1995 }
1996 default:
1997 SkASSERT(0);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001998 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001999 break;
2000 case SkPath::kQuad_Verb:
2001 switch (wn.verb()) {
2002 case SkPath::kLine_Verb: {
2003 pts = QuadLineIntersect(wt.fPts, wn.fPts, ts);
2004 break;
2005 }
2006 case SkPath::kQuad_Verb: {
2007 pts = QuadIntersect(wt.fPts, wn.fPts, ts);
2008 break;
2009 }
2010 case SkPath::kCubic_Verb: {
2011 // FIXME: promote quad to cubic
2012 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
2013 break;
2014 }
2015 default:
2016 SkASSERT(0);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002017 }
caryclark@google.coma5764232012-03-28 16:20:21 +00002018 break;
2019 case SkPath::kCubic_Verb:
2020 switch (wn.verb()) {
2021 case SkPath::kLine_Verb: {
2022 pts = CubicLineIntersect(wt.fPts, wn.fPts, ts);
2023 break;
2024 }
2025 case SkPath::kQuad_Verb: {
2026 // FIXME: promote quad to cubic
2027 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
2028 break;
2029 }
2030 case SkPath::kCubic_Verb: {
2031 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
2032 break;
2033 }
2034 default:
2035 SkASSERT(0);
2036 }
2037 break;
2038 default:
2039 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002040 }
caryclark@google.coma5764232012-03-28 16:20:21 +00002041 test->add(ts.fT[swap], pts, wt.verbIndex());
2042 next->add(ts.fT[!swap], pts, wn.verbIndex());
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002043 } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
2044 } while (nextPtr != lastPtr);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002045 }
2046}
2047
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002048static InEdge** advanceEdges(SkTDArray<ActiveEdge>* activeEdges,
caryclark@google.com6008c6562012-02-15 22:01:16 +00002049 InEdge** currentPtr, InEdge** lastPtr, SkScalar y) {
2050 InEdge** testPtr = currentPtr - 1;
2051 while (++testPtr != lastPtr) {
2052 if ((*testPtr)->fBounds.fBottom > y) {
2053 continue;
2054 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002055 if (activeEdges) {
2056 InEdge* test = *testPtr;
2057 ActiveEdge* activePtr = activeEdges->begin() - 1;
2058 ActiveEdge* lastActive = activeEdges->end();
2059 while (++activePtr != lastActive) {
2060 if (activePtr->fWorkEdge.fEdge == test) {
2061 activeEdges->remove(activePtr - activeEdges->begin());
2062 break;
2063 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002064 }
2065 }
2066 if (testPtr == currentPtr) {
2067 ++currentPtr;
2068 }
2069 }
2070 return currentPtr;
2071}
2072
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002073// OPTIMIZE: inline?
2074static HorizontalEdge** advanceHorizontal(HorizontalEdge** edge, SkScalar y) {
2075 while ((*edge)->fY < y) {
2076 ++edge;
2077 }
2078 return edge;
2079}
2080
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002081// compute bottom taking into account any intersected edges
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002082static SkScalar computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
2083 SkScalar y, SkScalar bottom) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002084 ActiveEdge* activePtr = activeEdges.begin() - 1;
2085 ActiveEdge* lastActive = activeEdges.end();
2086 while (++activePtr != lastActive) {
2087 const InEdge* test = activePtr->fWorkEdge.fEdge;
2088 if (!test->fContainsIntercepts) {
2089 continue;
2090 }
2091 WorkEdge wt;
2092 wt.init(test);
2093 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002094 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
caryclark@google.com4917f172012-03-05 22:01:21 +00002095 if (intercepts.fTopIntercepts > 1) {
2096 SkScalar yTop = wt.fPts[0].fY;
2097 if (yTop > y && bottom > yTop) {
2098 bottom = yTop;
2099 }
2100 }
2101 if (intercepts.fBottomIntercepts > 1) {
2102 SkScalar yBottom = wt.fPts[wt.verb()].fY;
2103 if (yBottom > y && bottom > yBottom) {
2104 bottom = yBottom;
2105 }
2106 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002107 const SkTDArray<double>& fTs = intercepts.fTs;
2108 size_t count = fTs.count();
2109 for (size_t index = 0; index < count; ++index) {
caryclark@google.coma5764232012-03-28 16:20:21 +00002110 SkScalar yIntercept;
2111 switch (wt.verb()) {
2112 case SkPath::kLine_Verb: {
2113 yIntercept = LineYAtT(wt.fPts, fTs[index]);
2114 break;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002115 }
caryclark@google.coma5764232012-03-28 16:20:21 +00002116 case SkPath::kQuad_Verb: {
2117 yIntercept = QuadYAtT(wt.fPts, fTs[index]);
2118 break;
2119 }
2120 case SkPath::kCubic_Verb: {
2121 yIntercept = CubicYAtT(wt.fPts, fTs[index]);
2122 break;
2123 }
2124 default:
2125 SkASSERT(0); // should never get here
2126 }
2127 if (yIntercept > y && bottom > yIntercept) {
2128 bottom = yIntercept;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002129 }
2130 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002131 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002132 }
caryclark@google.com198e0542012-03-30 18:47:02 +00002133#if DEBUG_BOTTOM
2134 SkDebugf("%s bottom=%1.9g\n", __FUNCTION__, bottom);
2135#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002136 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002137}
2138
2139static SkScalar findBottom(InEdge** currentPtr,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002140 InEdge** edgeListEnd, SkTDArray<ActiveEdge>* activeEdges, SkScalar y,
caryclark@google.com6008c6562012-02-15 22:01:16 +00002141 bool asFill, InEdge**& testPtr) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002142 InEdge* current = *currentPtr;
2143 SkScalar bottom = current->fBounds.fBottom;
caryclark@google.com752b60e2012-03-22 21:11:17 +00002144
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002145 // find the list of edges that cross y
caryclark@google.com6008c6562012-02-15 22:01:16 +00002146 InEdge* test = *testPtr;
2147 while (testPtr != edgeListEnd) {
2148 SkScalar testTop = test->fBounds.fTop;
2149 if (bottom <= testTop) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002150 break;
2151 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002152 SkScalar testBottom = test->fBounds.fBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002153 // OPTIMIZATION: Shortening the bottom is only interesting when filling
2154 // and when the edge is to the left of a longer edge. If it's a framing
2155 // edge, or part of the right, it won't effect the longer edges.
caryclark@google.com6008c6562012-02-15 22:01:16 +00002156 if (testTop > y) {
2157 bottom = testTop;
2158 break;
2159 }
2160 if (y < testBottom) {
2161 if (bottom > testBottom) {
2162 bottom = testBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002163 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002164 if (activeEdges) {
2165 addToActive(*activeEdges, test);
2166 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002167 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002168 test = *++testPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002169 }
caryclark@google.com198e0542012-03-30 18:47:02 +00002170#if DEBUG_BOTTOM
2171 SkDebugf("%s %d bottom=%1.9g\n", __FUNCTION__, activeEdges ? 2 : 1, bottom);
2172#endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002173 return bottom;
2174}
2175
2176static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
2177 SkTDArray<InEdge*>& edgeList) {
2178 size_t edgeCount = edges.count();
2179 if (edgeCount == 0) {
2180 return;
2181 }
caryclark@google.comfb173422012-04-10 18:28:55 +00002182 int id = 0;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002183 for (size_t index = 0; index < edgeCount; ++index) {
caryclark@google.comfb173422012-04-10 18:28:55 +00002184 InEdge& edge = edges[index];
2185 if (!edge.fWinding) {
2186 continue;
2187 }
2188 edge.fID = ++id;
2189 *edgeList.append() = &edge;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002190 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002191 *edgeList.append() = &edgeSentinel;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002192 QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002193}
2194
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002195static void makeHorizontalList(SkTDArray<HorizontalEdge>& edges,
2196 HorizontalEdge& edgeSentinel, SkTDArray<HorizontalEdge*>& edgeList) {
2197 size_t edgeCount = edges.count();
2198 if (edgeCount == 0) {
2199 return;
2200 }
2201 for (size_t index = 0; index < edgeCount; ++index) {
2202 *edgeList.append() = &edges[index];
2203 }
2204 edgeSentinel.fLeft = edgeSentinel.fRight = edgeSentinel.fY = SK_ScalarMax;
2205 *edgeList.append() = &edgeSentinel;
2206 QSort<HorizontalEdge>(edgeList.begin(), edgeList.end() - 1);
2207}
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002208
2209static void skipCoincidence(int lastWinding, int winding, int windingMask,
2210 ActiveEdge* activePtr, ActiveEdge* firstCoincident) {
2211 if (((lastWinding & windingMask) == 0) ^ (winding & windingMask) != 0) {
2212 return;
2213 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002214 // FIXME: ? shouldn't this be if (lastWinding & windingMask) ?
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002215 if (lastWinding) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002216#if DEBUG_ADJUST_COINCIDENT
2217 SkDebugf("%s edge=%d 1 set skip=false\n", __FUNCTION__, activePtr->ID());
2218#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002219 activePtr->fSkip = false;
2220 } else {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002221#if DEBUG_ADJUST_COINCIDENT
2222 SkDebugf("%s edge=%d 2 set skip=false\n", __FUNCTION__, firstCoincident->ID());
2223#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002224 firstCoincident->fSkip = false;
2225 }
2226}
2227
caryclark@google.com6008c6562012-02-15 22:01:16 +00002228static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002229 SkTDArray<ActiveEdge*>& edgeList, SkScalar y) {
caryclark@google.com6008c6562012-02-15 22:01:16 +00002230 size_t edgeCount = activeEdges.count();
2231 if (edgeCount == 0) {
2232 return;
2233 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002234#if DEBUG_SORT_HORIZONTAL
caryclark@google.comd88e0892012-03-27 13:23:51 +00002235 const int tab = 3; // FIXME: debugging only
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002236 SkDebugf("%s y=%1.9g\n", __FUNCTION__, y);
2237#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00002238 size_t index;
2239 for (index = 0; index < edgeCount; ++index) {
2240 ActiveEdge& activeEdge = activeEdges[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002241 do {
2242 activeEdge.calcLeft(y);
2243 // skip segments that don't span y
2244 if (activeEdge.fAbove != activeEdge.fBelow) {
2245 break;
2246 }
2247 if (activeEdge.fDone) {
2248#if DEBUG_SORT_HORIZONTAL
2249 SkDebugf("%*s edge=%d done\n", tab, "", activeEdge.ID());
2250#endif
2251 goto nextEdge;
2252 }
2253#if DEBUG_SORT_HORIZONTAL
2254 SkDebugf("%*s edge=%d above==below\n", tab, "", activeEdge.ID());
2255#endif
2256 } while (activeEdge.advanceT() || activeEdge.advance());
2257#if DEBUG_SORT_HORIZONTAL
2258 SkDebugf("%*s edge=%d above=(%1.9g,%1.9g) (%1.9g) below=(%1.9g,%1.9g)"
2259 " (%1.9g)\n", tab, "", activeEdge.ID(),
2260 activeEdge.fAbove.fX, activeEdge.fAbove.fY, activeEdge.fTAbove,
2261 activeEdge.fBelow.fX, activeEdge.fBelow.fY, activeEdge.fTBelow);
2262#endif
2263 activeEdge.fSkip = activeEdge.fCloseCall = activeEdge.fFixBelow = false;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002264 *edgeList.append() = &activeEdge;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002265nextEdge:
2266 ;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002267 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002268 QSort<ActiveEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002269}
2270
2271// remove coincident edges
2272// OPTIMIZE: remove edges? This is tricky because the current logic expects
2273// the winding count to be maintained while skipping coincident edges. In
2274// addition to removing the coincident edges, the remaining edges would need
2275// to have a different winding value, possibly different per intercept span.
2276static SkScalar adjustCoincident(SkTDArray<ActiveEdge*>& edgeList,
2277 int windingMask, SkScalar y, SkScalar bottom, OutEdgeBuilder& outBuilder)
2278{
2279#if DEBUG_ADJUST_COINCIDENT
2280 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
2281#endif
2282 size_t edgeCount = edgeList.count();
2283 if (edgeCount == 0) {
2284 return bottom;
2285 }
caryclark@google.com78e17132012-04-17 11:40:34 +00002286 ActiveEdge* activePtr, * nextPtr = edgeList[0];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002287 size_t index;
2288 bool foundCoincident = false;
2289 int firstIndex = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002290 for (index = 1; index < edgeCount; ++index) {
caryclark@google.com78e17132012-04-17 11:40:34 +00002291 activePtr = nextPtr;
2292 nextPtr = edgeList[index];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002293 if (firstIndex != index - 1 && activePtr->fVerb > SkPath::kLine_Verb
2294 && nextPtr->fVerb == SkPath::kLine_Verb
2295 && activePtr->isUnordered(nextPtr)) {
2296 // swap the line with the curve
2297 // back up to the previous edge and retest
2298 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
2299 SkASSERT(index > 1);
2300 index -= 2;
2301 nextPtr = edgeList[index];
2302 continue;
caryclark@google.com78e17132012-04-17 11:40:34 +00002303 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002304 bool closeCall = false;
2305 activePtr->fCoincident = firstIndex;
caryclark@google.com78e17132012-04-17 11:40:34 +00002306 if (activePtr->isCoincidentWith(nextPtr)
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002307 || (closeCall = activePtr->tooCloseToCall(nextPtr))) {
2308 activePtr->fSkip = nextPtr->fSkip = foundCoincident = true;
2309 activePtr->fCloseCall = nextPtr->fCloseCall = closeCall;
caryclark@google.com78e17132012-04-17 11:40:34 +00002310 } else if (activePtr->isUnordered(nextPtr)) {
2311 foundCoincident = true;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002312 } else {
2313 firstIndex = index;
2314 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002315 }
caryclark@google.com78e17132012-04-17 11:40:34 +00002316 nextPtr->fCoincident = firstIndex;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002317 if (!foundCoincident) {
2318 return bottom;
2319 }
2320 int winding = 0;
caryclark@google.com78e17132012-04-17 11:40:34 +00002321 nextPtr = edgeList[0];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002322 for (index = 1; index < edgeCount; ++index) {
2323 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002324 winding += activePtr->fWorkEdge.winding();
caryclark@google.com78e17132012-04-17 11:40:34 +00002325 activePtr = nextPtr;
2326 nextPtr = edgeList[index];
2327 SkASSERT(activePtr == edgeList[index - 1]);
2328 SkASSERT(nextPtr == edgeList[index]);
2329 if (activePtr->fCoincident != nextPtr->fCoincident) {
2330 continue;
2331 }
2332 // the coincident edges may not have been sorted above -- advance
2333 // the edges and resort if needed
2334 // OPTIMIZE: if sorting is done incrementally as new edges are added
2335 // and not all at once as is done here, fold this test into the
2336 // current less than test.
2337 while ((!activePtr->fSkip || !nextPtr->fSkip)
2338 && activePtr->fCoincident == nextPtr->fCoincident) {
2339 if (activePtr->swapUnordered(nextPtr, bottom)) {
2340 winding -= activePtr->fWorkEdge.winding();
2341 SkASSERT(activePtr == edgeList[index - 1]);
2342 SkASSERT(nextPtr == edgeList[index]);
2343 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
2344 if (--index == 0) {
2345 winding += activePtr->fWorkEdge.winding();
2346 break;
2347 }
2348 // back up one
2349 activePtr = edgeList[index - 1];
2350 continue;
2351 }
2352 SkASSERT(activePtr == edgeList[index - 1]);
2353 SkASSERT(nextPtr == edgeList[index]);
2354 break;
2355 }
2356 if (activePtr->fSkip && nextPtr->fSkip) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002357 if (activePtr->fCloseCall ? activePtr->swapClose(nextPtr,
2358 priorWinding, winding, windingMask)
2359 : activePtr->swapCoincident(nextPtr, bottom)) {
caryclark@google.com4917f172012-03-05 22:01:21 +00002360 winding -= activePtr->fWorkEdge.winding();
caryclark@google.com78e17132012-04-17 11:40:34 +00002361 SkASSERT(activePtr == edgeList[index - 1]);
2362 SkASSERT(nextPtr == edgeList[index]);
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002363 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
2364 SkTSwap<ActiveEdge*>(activePtr, nextPtr);
caryclark@google.com4917f172012-03-05 22:01:21 +00002365 winding += activePtr->fWorkEdge.winding();
caryclark@google.com78e17132012-04-17 11:40:34 +00002366 SkASSERT(activePtr == edgeList[index - 1]);
2367 SkASSERT(nextPtr == edgeList[index]);
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002368 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002369 }
2370 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002371 int firstCoincidentWinding = 0;
2372 ActiveEdge* firstCoincident = NULL;
2373 winding = 0;
2374 activePtr = edgeList[0];
2375 for (index = 1; index < edgeCount; ++index) {
2376 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002377 winding += activePtr->fWorkEdge.winding();
caryclark@google.com78e17132012-04-17 11:40:34 +00002378 nextPtr = edgeList[index];
2379 if (activePtr->fSkip && nextPtr->fSkip
2380 && activePtr->fCoincident == nextPtr->fCoincident) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002381 if (!firstCoincident) {
2382 firstCoincident = activePtr;
2383 firstCoincidentWinding = priorWinding;
2384 }
2385 if (activePtr->fCloseCall) {
2386 // If one of the edges has already been added to out as a non
2387 // coincident edge, trim it back to the top of this span
2388 if (outBuilder.trimLine(y, activePtr->ID())) {
2389 activePtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002390 #if DEBUG_ADJUST_COINCIDENT
2391 SkDebugf("%s 1 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
2392 __FUNCTION__, activePtr->ID(), y, activePtr->fYBottom);
2393 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002394 activePtr->fYBottom = y;
2395 }
2396 if (outBuilder.trimLine(y, nextPtr->ID())) {
2397 nextPtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002398 #if DEBUG_ADJUST_COINCIDENT
2399 SkDebugf("%s 2 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
2400 __FUNCTION__, nextPtr->ID(), y, nextPtr->fYBottom);
2401 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002402 nextPtr->fYBottom = y;
2403 }
2404 // add missing t values so edges can be the same length
2405 SkScalar testY = activePtr->fBelow.fY;
2406 nextPtr->addTatYBelow(testY);
2407 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002408 #if DEBUG_ADJUST_COINCIDENT
2409 SkDebugf("%s 3 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
2410 __FUNCTION__, activePtr->ID(), testY, bottom);
2411 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002412 bottom = testY;
2413 }
2414 testY = nextPtr->fBelow.fY;
2415 activePtr->addTatYBelow(testY);
2416 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002417 #if DEBUG_ADJUST_COINCIDENT
2418 SkDebugf("%s 4 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
2419 __FUNCTION__, nextPtr->ID(), testY, bottom);
2420 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002421 bottom = testY;
2422 }
2423 }
2424 } else if (firstCoincident) {
2425 skipCoincidence(firstCoincidentWinding, winding, windingMask,
2426 activePtr, firstCoincident);
2427 firstCoincident = NULL;
2428 }
2429 activePtr = nextPtr;
2430 }
2431 if (firstCoincident) {
2432 winding += activePtr->fWorkEdge.winding();
2433 skipCoincidence(firstCoincidentWinding, winding, windingMask, activePtr,
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002434 firstCoincident);
2435 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002436 // fix up the bottom for close call edges. OPTIMIZATION: maybe this could
2437 // be in the loop above, but moved here since loop above reads fBelow and
2438 // it felt unsafe to write it in that loop
2439 for (index = 0; index < edgeCount; ++index) {
2440 (edgeList[index])->fixBelow();
2441 }
2442 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002443}
2444
2445// stitch edge and t range that satisfies operation
caryclark@google.com6008c6562012-02-15 22:01:16 +00002446static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
caryclark@google.com752b60e2012-03-22 21:11:17 +00002447 SkScalar bottom, int windingMask, bool fill, OutEdgeBuilder& outBuilder) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002448 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002449 ActiveEdge** activeHandle = edgeList.begin() - 1;
2450 ActiveEdge** lastActive = edgeList.end();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002451 const int tab = 7; // FIXME: debugging only
caryclark@google.com78e17132012-04-17 11:40:34 +00002452#if DEBUG_STITCH_EDGE
2453 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
2454#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00002455 while (++activeHandle != lastActive) {
2456 ActiveEdge* activePtr = *activeHandle;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002457 const WorkEdge& wt = activePtr->fWorkEdge;
2458 int lastWinding = winding;
2459 winding += wt.winding();
caryclark@google.com78e17132012-04-17 11:40:34 +00002460#if DEBUG_STITCH_EDGE
2461 SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d"
2462 " above=%1.9g below=%1.9g\n",
2463 tab-4, "", activePtr->ID(), lastWinding,
2464 winding, activePtr->fSkip, activePtr->fCloseCall,
2465 activePtr->fTAbove, activePtr->fTBelow);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002466#endif
caryclark@google.com752b60e2012-03-22 21:11:17 +00002467 if (activePtr->done(bottom)) {
caryclark@google.com78e17132012-04-17 11:40:34 +00002468#if DEBUG_STITCH_EDGE
2469 SkDebugf("%*s fDone=%d || fYBottom=%1.9g >= bottom\n", tab, "",
2470 activePtr->fDone, activePtr->fYBottom);
2471#endif
caryclark@google.com752b60e2012-03-22 21:11:17 +00002472 continue;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002473 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00002474 int opener = (lastWinding & windingMask) == 0;
2475 bool closer = (winding & windingMask) == 0;
2476 SkASSERT(!opener | !closer);
2477 bool inWinding = opener | closer;
caryclark@google.coma5764232012-03-28 16:20:21 +00002478 SkPoint clippedPts[4];
caryclark@google.com4917f172012-03-05 22:01:21 +00002479 const SkPoint* clipped = NULL;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002480 bool moreToDo, aboveBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002481 do {
2482 double currentT = activePtr->t();
2483 const SkPoint* points = wt.fPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002484 double nextT;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002485 SkPath::Verb verb = activePtr->fVerb;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002486 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002487 nextT = activePtr->nextT();
caryclark@google.coma5764232012-03-28 16:20:21 +00002488 // FIXME: obtuse: want efficient way to say
2489 // !currentT && currentT != 1 || !nextT && nextT != 1
2490 if (currentT * nextT != 0 || currentT + nextT != 1) {
2491 // OPTIMIZATION: if !inWinding, we only need
2492 // clipped[1].fY
2493 switch (verb) {
2494 case SkPath::kLine_Verb:
2495 LineSubDivide(points, currentT, nextT, clippedPts);
2496 break;
2497 case SkPath::kQuad_Verb:
2498 QuadSubDivide(points, currentT, nextT, clippedPts);
2499 break;
2500 case SkPath::kCubic_Verb:
2501 CubicSubDivide(points, currentT, nextT, clippedPts);
2502 break;
2503 default:
2504 SkASSERT(0);
2505 break;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002506 }
caryclark@google.coma5764232012-03-28 16:20:21 +00002507 clipped = clippedPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002508 } else {
caryclark@google.coma5764232012-03-28 16:20:21 +00002509 clipped = points;
2510 }
2511 if (inWinding && !activePtr->fSkip && (fill ? clipped[0].fY
2512 != clipped[verb].fY : clipped[0] != clipped[verb])) {
caryclark@google.comfb173422012-04-10 18:28:55 +00002513#if DEBUG_STITCH_EDGE
2514 SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d"
2515 " v=%d t=(%1.9g,%1.9g)\n", tab, "",
2516 kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
2517 clipped[verb].fX, clipped[verb].fY,
2518 activePtr->ID(),
2519 activePtr->fWorkEdge.fVerb
2520 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
2521 currentT, nextT);
2522#endif
caryclark@google.coma5764232012-03-28 16:20:21 +00002523 outBuilder.addCurve(clipped, (SkPath::Verb) verb,
2524 activePtr->fWorkEdge.fEdge->fID,
2525 activePtr->fCloseCall);
2526 } else {
caryclark@google.comfb173422012-04-10 18:28:55 +00002527#if DEBUG_STITCH_EDGE
2528 SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g"
2529 " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
2530 kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
2531 clipped[verb].fX, clipped[verb].fY,
2532 activePtr->ID(),
2533 activePtr->fWorkEdge.fVerb
2534 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
2535 currentT, nextT);
2536#endif
caryclark@google.coma5764232012-03-28 16:20:21 +00002537 }
2538 // by advancing fAbove/fBelow, the next call to sortHorizontal
2539 // will use these values if they're still valid instead of
2540 // recomputing
caryclark@google.com78e17132012-04-17 11:40:34 +00002541 if (clipped[verb].fY > activePtr->fBelow.fY
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002542 && bottom >= activePtr->fBelow.fY
2543 && verb == SkPath::kLine_Verb) {
caryclark@google.coma5764232012-03-28 16:20:21 +00002544 activePtr->fAbove = activePtr->fBelow;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002545 activePtr->fBelow = activePtr->fTangent = clipped[verb];
caryclark@google.com78e17132012-04-17 11:40:34 +00002546 activePtr->fTAbove = activePtr->fTBelow < 1
2547 ? activePtr->fTBelow : 0;
caryclark@google.coma5764232012-03-28 16:20:21 +00002548 activePtr->fTBelow = nextT;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002549 }
2550 currentT = nextT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002551 moreToDo = activePtr->advanceT();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002552 activePtr->fYBottom = clipped[verb].fY; // was activePtr->fCloseCall ? bottom :
2553
2554 // clearing the fSkip/fCloseCall bit here means that trailing edges
2555 // fall out of sync, if one edge is long and another is a series of short pieces
2556 // if fSkip/fCloseCall is set, need to recompute coincidence/too-close-to-call
2557 // after advancing
2558 // another approach would be to restrict bottom to smaller part of close call
2559 // maybe this is already happening with coincidence when intersection is computed,
2560 // and needs to be added to the close call computation as well
2561 // this is hard to do because that the bottom is important is not known when
2562 // the lines are intersected; only when the computation for edge sorting is done
2563 // does the need for new bottoms become apparent.
2564 // maybe this is good incentive to scrap the current sort and do an insertion
2565 // sort that can take this into consideration when the x value is computed
2566
2567 // FIXME: initialized in sortHorizontal, cleared here as well so
2568 // that next edge is not skipped -- but should skipped edges ever
2569 // continue? (probably not)
caryclark@google.com752b60e2012-03-22 21:11:17 +00002570 aboveBottom = clipped[verb].fY < bottom;
2571 if (clipped[0].fY != clipped[verb].fY) {
2572 activePtr->fSkip = false;
2573 activePtr->fCloseCall = false;
2574 aboveBottom &= !activePtr->fCloseCall;
caryclark@google.com78e17132012-04-17 11:40:34 +00002575 }
2576#if DEBUG_STITCH_EDGE
2577 else {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002578 if (activePtr->fSkip || activePtr->fCloseCall) {
caryclark@google.com78e17132012-04-17 11:40:34 +00002579 SkDebugf("%s skip or close == %1.9g\n", __FUNCTION__,
2580 clippedPts[0].fY);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002581 }
2582 }
caryclark@google.com78e17132012-04-17 11:40:34 +00002583#endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002584 } while (moreToDo & aboveBottom);
2585 } while ((moreToDo || activePtr->advance()) & aboveBottom);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002586 }
2587}
2588
caryclark@google.com198e0542012-03-30 18:47:02 +00002589static void dumpEdgeList(const SkTDArray<InEdge*>& edgeList,
2590 const InEdge& edgeSentinel) {
2591#if DEBUG_DUMP
2592 InEdge** debugPtr = edgeList.begin();
2593 do {
2594 (*debugPtr++)->dump();
2595 } while (*debugPtr != &edgeSentinel);
2596#endif
2597}
2598
caryclark@google.comc6825902012-02-03 22:07:47 +00002599void simplify(const SkPath& path, bool asFill, SkPath& simple) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002600 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
2601 int windingMask = (path.getFillType() & 1) ? 1 : -1;
2602 simple.reset();
2603 simple.setFillType(SkPath::kEvenOdd_FillType);
caryclark@google.comc6825902012-02-03 22:07:47 +00002604 // turn path into list of edges increasing in y
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002605 // if an edge is a quad or a cubic with a y extrema, note it, but leave it
2606 // unbroken. Once we have a list, sort it, then walk the list (walk edges
2607 // twice that have y extrema's on top) and detect crossings -- look for raw
2608 // bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +00002609 SkTArray<InEdge> edges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002610 SkTDArray<HorizontalEdge> horizontalEdges;
2611 InEdgeBuilder builder(path, asFill, edges, horizontalEdges);
caryclark@google.com6680fb12012-02-07 22:10:51 +00002612 SkTDArray<InEdge*> edgeList;
caryclark@google.com6680fb12012-02-07 22:10:51 +00002613 InEdge edgeSentinel;
caryclark@google.comfb173422012-04-10 18:28:55 +00002614 edgeSentinel.reset();
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002615 makeEdgeList(edges, edgeSentinel, edgeList);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002616 SkTDArray<HorizontalEdge*> horizontalList;
2617 HorizontalEdge horizontalSentinel;
2618 makeHorizontalList(horizontalEdges, horizontalSentinel, horizontalList);
caryclark@google.com6680fb12012-02-07 22:10:51 +00002619 InEdge** currentPtr = edgeList.begin();
caryclark@google.com4917f172012-03-05 22:01:21 +00002620 if (!currentPtr) {
2621 return;
2622 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002623 // find all intersections between edges
2624// beyond looking for horizontal intercepts, we need to know if any active edges
2625// intersect edges below 'bottom', but above the active edge segment.
2626// maybe it makes more sense to compute all intercepts before doing anything
2627// else, since the intercept list is long-lived, at least in the current design.
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002628 SkScalar y = (*currentPtr)->fBounds.fTop;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002629 HorizontalEdge** currentHorizontal = horizontalList.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00002630 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +00002631 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002632 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002633 NULL, y, asFill, lastPtr);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002634 if (lastPtr > currentPtr) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002635 if (currentHorizontal) {
2636 if ((*currentHorizontal)->fY < SK_ScalarMax) {
2637 addBottomT(currentPtr, lastPtr, currentHorizontal);
2638 }
2639 currentHorizontal = advanceHorizontal(currentHorizontal, bottom);
2640 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00002641 addIntersectingTs(currentPtr, lastPtr);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002642 }
2643 y = bottom;
2644 currentPtr = advanceEdges(NULL, currentPtr, lastPtr, y);
2645 } while (*currentPtr != &edgeSentinel);
caryclark@google.com198e0542012-03-30 18:47:02 +00002646 // if a quadratic or cubic now has an intermediate T value, see if the Ts
2647 // on either side cause the Y values to monotonically increase. If not, split
2648 // the curve at the new T.
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002649
2650 // try an alternate approach which does not split curves or stitch edges
2651 // (may still need adjustCoincident, though)
2652 // the idea is to output non-intersecting contours, then figure out their
2653 // respective winding contribution
2654 // each contour will need to know whether it is CW or CCW, and then whether
2655 // a ray from that contour hits any a contour that contains it. The ray can
2656 // move to the left and then arbitrarily move up or down (as long as it never
2657 // moves to the right) to find a reference sibling contour or containing
2658 // contour. If the contour is part of an intersection, the companion contour
2659 // that is part of the intersection can determine the containership.
caryclark@google.com198e0542012-03-30 18:47:02 +00002660 if (builder.containsCurves()) {
2661 currentPtr = edgeList.begin();
2662 SkTArray<InEdge> splits;
2663 do {
caryclark@google.comfb173422012-04-10 18:28:55 +00002664 (*currentPtr)->splitInflectionPts(splits);
caryclark@google.com198e0542012-03-30 18:47:02 +00002665 } while (*++currentPtr != &edgeSentinel);
2666 if (splits.count()) {
caryclark@google.com198e0542012-03-30 18:47:02 +00002667 for (int index = 0; index < splits.count(); ++index) {
2668 edges.push_back(splits[index]);
2669 }
caryclark@google.comfb173422012-04-10 18:28:55 +00002670 edgeList.reset();
caryclark@google.com198e0542012-03-30 18:47:02 +00002671 makeEdgeList(edges, edgeSentinel, edgeList);
2672 }
2673 }
2674 dumpEdgeList(edgeList, edgeSentinel);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002675 // walk the sorted edges from top to bottom, computing accumulated winding
2676 SkTDArray<ActiveEdge> activeEdges;
2677 OutEdgeBuilder outBuilder(asFill);
2678 currentPtr = edgeList.begin();
2679 y = (*currentPtr)->fBounds.fTop;
2680 do {
2681 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
2682 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
2683 &activeEdges, y, asFill, lastPtr);
2684 if (lastPtr > currentPtr) {
2685 bottom = computeInterceptBottom(activeEdges, y, bottom);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002686 SkTDArray<ActiveEdge*> activeEdgeList;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002687 sortHorizontal(activeEdges, activeEdgeList, y);
2688 bottom = adjustCoincident(activeEdgeList, windingMask, y, bottom,
2689 outBuilder);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002690 stitchEdge(activeEdgeList, y, bottom, windingMask, asFill, outBuilder);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002691 }
caryclark@google.comc6825902012-02-03 22:07:47 +00002692 y = bottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002693 // OPTIMIZATION: as edges expire, InEdge allocations could be released
2694 currentPtr = advanceEdges(&activeEdges, currentPtr, lastPtr, y);
caryclark@google.comc6825902012-02-03 22:07:47 +00002695 } while (*currentPtr != &edgeSentinel);
caryclark@google.comc6825902012-02-03 22:07:47 +00002696 // assemble output path from string of pts, verbs
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002697 outBuilder.bridge();
2698 outBuilder.assemble(simple);
caryclark@google.comc6825902012-02-03 22:07:47 +00002699}