blob: 1bf3c8bf5091e1b140265548675f9d2c7d88ff51 [file] [log] [blame]
caryclark@google.comc6825902012-02-03 22:07:47 +00001
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
caryclark@google.com6680fb12012-02-07 22:10:51 +00009#include "CurveIntersection.h"
caryclark@google.coma5764232012-03-28 16:20:21 +000010#include "Intersections.h"
caryclark@google.comc6825902012-02-03 22:07:47 +000011#include "LineIntersection.h"
12#include "SkPath.h"
13#include "SkRect.h"
14#include "SkTArray.h"
15#include "SkTDArray.h"
caryclark@google.comd88e0892012-03-27 13:23:51 +000016#include "ShapeOps.h"
caryclark@google.comc6825902012-02-03 22:07:47 +000017#include "TSearch.h"
18
caryclark@google.com78e17132012-04-17 11:40:34 +000019#undef SkASSERT
20#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000021
caryclark@google.com78e17132012-04-17 11:40:34 +000022// FIXME: remove once debugging is complete
23#if 0 // set to 1 for no debugging whatsoever
24
25const bool gRunTestsInOneThread = true;
26
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000027#define DEBUG_ACTIVE_LESS_THAN 0
caryclark@google.com78e17132012-04-17 11:40:34 +000028#define DEBUG_ADD 0
29#define DEBUG_ADD_BOTTOM_TS 0
30#define DEBUG_ADD_INTERSECTING_TS 0
31#define DEBUG_ADJUST_COINCIDENT 0
caryclark@google.comfb173422012-04-10 18:28:55 +000032#define DEBUG_ASSEMBLE 0
caryclark@google.com78e17132012-04-17 11:40:34 +000033#define DEBUG_BOTTOM 0
caryclark@google.comfb173422012-04-10 18:28:55 +000034#define DEBUG_BRIDGE 0
caryclark@google.com78e17132012-04-17 11:40:34 +000035#define DEBUG_DUMP 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000036#define DEBUG_SORT_HORIZONTAL 0
37#define DEBUG_OUT 0
38#define DEBUG_OUT_LESS_THAN 0
caryclark@google.com198e0542012-03-30 18:47:02 +000039#define DEBUG_SPLIT 0
caryclark@google.comfb173422012-04-10 18:28:55 +000040#define DEBUG_STITCH_EDGE 0
caryclark@google.com78e17132012-04-17 11:40:34 +000041#define DEBUG_TRIM_LINE 0
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000042
caryclark@google.com78e17132012-04-17 11:40:34 +000043#else
44
45const bool gRunTestsInOneThread = true;
46
caryclark@google.coma5764232012-03-28 16:20:21 +000047#define DEBUG_ACTIVE_LESS_THAN 0
caryclark@google.com78e17132012-04-17 11:40:34 +000048#define DEBUG_ADD 01
49#define DEBUG_ADD_BOTTOM_TS 0
50#define DEBUG_ADD_INTERSECTING_TS 0
51#define DEBUG_ADJUST_COINCIDENT 1
caryclark@google.comfb173422012-04-10 18:28:55 +000052#define DEBUG_ASSEMBLE 1
caryclark@google.com78e17132012-04-17 11:40:34 +000053#define DEBUG_BOTTOM 0
caryclark@google.comfb173422012-04-10 18:28:55 +000054#define DEBUG_BRIDGE 1
caryclark@google.com78e17132012-04-17 11:40:34 +000055#define DEBUG_DUMP 1
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000056#define DEBUG_SORT_HORIZONTAL 01
57#define DEBUG_OUT 01
58#define DEBUG_OUT_LESS_THAN 0
caryclark@google.com198e0542012-03-30 18:47:02 +000059#define DEBUG_SPLIT 1
caryclark@google.comfb173422012-04-10 18:28:55 +000060#define DEBUG_STITCH_EDGE 1
caryclark@google.com78e17132012-04-17 11:40:34 +000061#define DEBUG_TRIM_LINE 1
caryclark@google.com752b60e2012-03-22 21:11:17 +000062
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000063#endif
64
caryclark@google.comfb173422012-04-10 18:28:55 +000065#if DEBUG_ASSEMBLE || DEBUG_BRIDGE
66static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
67#endif
68#if DEBUG_STITCH_EDGE
69static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
70#endif
71
caryclark@google.com6680fb12012-02-07 22:10:51 +000072static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
caryclark@google.coma5764232012-03-28 16:20:21 +000073 Intersections& intersections) {
74 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
75 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
76 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
77}
78
79static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
80 Intersections& intersections) {
81 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
82 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +000083 intersect(aQuad, bLine, intersections);
84 return intersections.fUsed;
caryclark@google.coma5764232012-03-28 16:20:21 +000085}
86
87static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
88 Intersections& intersections) {
89 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
90 {a[3].fX, a[3].fY}};
91 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +000092 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
caryclark@google.coma5764232012-03-28 16:20:21 +000093}
94
95static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
96 Intersections& intersections) {
97 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
98 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 +000099 intersect(aQuad, bQuad, intersections);
100 return intersections.fUsed;
caryclark@google.coma5764232012-03-28 16:20:21 +0000101}
102
103static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
104 Intersections& intersections) {
105 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
106 {a[3].fX, a[3].fY}};
107 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
108 {b[3].fX, b[3].fY}};
caryclark@google.com198e0542012-03-30 18:47:02 +0000109 intersect(aCubic, bCubic, intersections);
110 return intersections.fUsed;
caryclark@google.comc6825902012-02-03 22:07:47 +0000111}
112
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000113static int LineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
114 SkScalar y, double aRange[2]) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000115 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000116 return horizontalLineIntersect(aLine, left, right, y, aRange);
caryclark@google.comc6825902012-02-03 22:07:47 +0000117}
118
caryclark@google.com198e0542012-03-30 18:47:02 +0000119static int QuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
120 SkScalar y, double aRange[3]) {
121 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
122 return horizontalIntersect(aQuad, left, right, y, aRange);
123}
124
125static int CubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
126 SkScalar y, double aRange[4]) {
127 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
128 {a[3].fX, a[3].fY}};
129 return horizontalIntersect(aCubic, left, right, y, aRange);
130}
131
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000132static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000133 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000134 double x, y;
caryclark@google.coma5764232012-03-28 16:20:21 +0000135 xy_at_t(line, t, x, y);
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000136 out->fX = SkDoubleToScalar(x);
137 out->fY = SkDoubleToScalar(y);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000138}
139
caryclark@google.coma5764232012-03-28 16:20:21 +0000140static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
141 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
142 double x, y;
143 xy_at_t(quad, t, x, y);
144 out->fX = SkDoubleToScalar(x);
145 out->fY = SkDoubleToScalar(y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000146}
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000147
caryclark@google.coma5764232012-03-28 16:20:21 +0000148static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
149 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
150 {a[3].fX, a[3].fY}};
151 double x, y;
152 xy_at_t(cubic, t, x, y);
153 out->fX = SkDoubleToScalar(x);
154 out->fY = SkDoubleToScalar(y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000155}
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000156
caryclark@google.com6680fb12012-02-07 22:10:51 +0000157static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000158 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com6680fb12012-02-07 22:10:51 +0000159 double y;
160 xy_at_t(aLine, t, *(double*) 0, y);
161 return SkDoubleToScalar(y);
162}
163
caryclark@google.coma5764232012-03-28 16:20:21 +0000164static SkScalar QuadYAtT(const SkPoint a[3], double t) {
165 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
166 double y;
167 xy_at_t(quad, t, *(double*) 0, y);
168 return SkDoubleToScalar(y);
169}
170
171static SkScalar CubicYAtT(const SkPoint a[4], double t) {
172 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
173 {a[3].fX, a[3].fY}};
174 double y;
175 xy_at_t(cubic, t, *(double*) 0, y);
176 return SkDoubleToScalar(y);
177}
178
caryclark@google.com6680fb12012-02-07 22:10:51 +0000179static void LineSubDivide(const SkPoint a[2], double startT, double endT,
180 SkPoint sub[2]) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000181 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.com6680fb12012-02-07 22:10:51 +0000182 _Line dst;
183 sub_divide(aLine, startT, endT, dst);
184 sub[0].fX = SkDoubleToScalar(dst[0].x);
185 sub[0].fY = SkDoubleToScalar(dst[0].y);
186 sub[1].fX = SkDoubleToScalar(dst[1].x);
187 sub[1].fY = SkDoubleToScalar(dst[1].y);
188}
189
caryclark@google.coma5764232012-03-28 16:20:21 +0000190static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
191 SkPoint sub[3]) {
caryclark@google.com78e17132012-04-17 11:40:34 +0000192 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
193 {a[2].fX, a[2].fY}};
caryclark@google.coma5764232012-03-28 16:20:21 +0000194 Quadratic dst;
195 sub_divide(aQuad, startT, endT, dst);
196 sub[0].fX = SkDoubleToScalar(dst[0].x);
197 sub[0].fY = SkDoubleToScalar(dst[0].y);
198 sub[1].fX = SkDoubleToScalar(dst[1].x);
199 sub[1].fY = SkDoubleToScalar(dst[1].y);
200 sub[2].fX = SkDoubleToScalar(dst[2].x);
201 sub[2].fY = SkDoubleToScalar(dst[2].y);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000202}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000203
caryclark@google.coma5764232012-03-28 16:20:21 +0000204static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
205 SkPoint sub[4]) {
caryclark@google.com78e17132012-04-17 11:40:34 +0000206 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
207 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
caryclark@google.coma5764232012-03-28 16:20:21 +0000208 Cubic dst;
209 sub_divide(aCubic, startT, endT, dst);
210 sub[0].fX = SkDoubleToScalar(dst[0].x);
211 sub[0].fY = SkDoubleToScalar(dst[0].y);
212 sub[1].fX = SkDoubleToScalar(dst[1].x);
213 sub[1].fY = SkDoubleToScalar(dst[1].y);
214 sub[2].fX = SkDoubleToScalar(dst[2].x);
215 sub[2].fY = SkDoubleToScalar(dst[2].y);
216 sub[3].fX = SkDoubleToScalar(dst[3].x);
217 sub[3].fY = SkDoubleToScalar(dst[3].y);
218}
caryclark@google.comfb173422012-04-10 18:28:55 +0000219
220static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
221 SkRect& bounds) {
222 SkPoint dst[3];
223 QuadSubDivide(a, startT, endT, dst);
224 bounds.fLeft = bounds.fRight = dst[0].fX;
225 bounds.fTop = bounds.fBottom = dst[0].fY;
226 for (int index = 1; index < 3; ++index) {
227 bounds.growToInclude(dst[index].fX, dst[index].fY);
228 }
229}
230
231static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
232 SkRect& bounds) {
233 SkPoint dst[4];
234 CubicSubDivide(a, startT, endT, dst);
235 bounds.fLeft = bounds.fRight = dst[0].fX;
236 bounds.fTop = bounds.fBottom = dst[0].fY;
237 for (int index = 1; index < 4; ++index) {
238 bounds.growToInclude(dst[index].fX, dst[index].fY);
239 }
240}
241
caryclark@google.com78e17132012-04-17 11:40:34 +0000242static SkPath::Verb QuadReduceOrder(SkPoint a[4]) {
243 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
244 {a[2].fX, a[2].fY}};
245 Quadratic dst;
246 int order = reduceOrder(aQuad, dst);
247 for (int index = 0; index < order; ++index) {
248 a[index].fX = SkDoubleToScalar(dst[index].x);
249 a[index].fY = SkDoubleToScalar(dst[index].y);
250 }
251 if (order == 1) { // FIXME: allow returning points, caller should discard
252 a[1] = a[0];
253 return (SkPath::Verb) order;
254 }
255 return (SkPath::Verb) (order - 1);
256}
257
258static SkPath::Verb CubicReduceOrder(SkPoint a[4]) {
259 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
260 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
261 Cubic dst;
262 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
263 for (int index = 0; index < order; ++index) {
264 a[index].fX = SkDoubleToScalar(dst[index].x);
265 a[index].fY = SkDoubleToScalar(dst[index].y);
266 }
267 if (order == 1) { // FIXME: allow returning points, caller should discard
268 a[1] = a[0];
269 return (SkPath::Verb) order;
270 }
271 return (SkPath::Verb) (order - 1);
272}
273
274static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
275 const SkPoint& below) {
276 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
277 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
278 return implicit_matches_ulps(aLine, bLine, 32);
279}
280
caryclark@google.comc6825902012-02-03 22:07:47 +0000281/*
282list of edges
283bounds for edge
284sort
285active T
286
287if a contour's bounds is outside of the active area, no need to create edges
288*/
289
290/* given one or more paths,
291 find the bounds of each contour, select the active contours
292 for each active contour, compute a set of edges
293 each edge corresponds to one or more lines and curves
294 leave edges unbroken as long as possible
295 when breaking edges, compute the t at the break but leave the control points alone
296
297 */
298
299void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray) {
300 SkPath::Iter iter(path, false);
301 SkPoint pts[4];
302 SkPath::Verb verb;
303 SkRect bounds;
304 bounds.setEmpty();
305 int count = 0;
306 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
307 switch (verb) {
308 case SkPath::kMove_Verb:
309 if (!bounds.isEmpty()) {
310 *boundsArray.append() = bounds;
311 }
312 bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
313 count = 0;
314 break;
315 case SkPath::kLine_Verb:
316 count = 1;
317 break;
318 case SkPath::kQuad_Verb:
319 count = 2;
320 break;
321 case SkPath::kCubic_Verb:
322 count = 3;
323 break;
324 case SkPath::kClose_Verb:
325 count = 0;
326 break;
327 default:
328 SkDEBUGFAIL("bad verb");
329 return;
330 }
331 for (int i = 1; i <= count; ++i) {
332 bounds.growToInclude(pts[i].fX, pts[i].fY);
333 }
334 }
335}
336
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000337static bool extendLine(const SkPoint line[2], const SkPoint& add) {
338 // FIXME: allow this to extend lines that have slopes that are nearly equal
339 SkScalar dx1 = line[1].fX - line[0].fX;
340 SkScalar dy1 = line[1].fY - line[0].fY;
341 SkScalar dx2 = add.fX - line[0].fX;
342 SkScalar dy2 = add.fY - line[0].fY;
343 return dx1 * dy2 == dx2 * dy1;
344}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000345
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000346// OPTIMIZATION: this should point to a list of input data rather than duplicating
347// the line data here. This would reduce the need to assemble the results.
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000348struct OutEdge {
349 bool operator<(const OutEdge& rh) const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000350 const SkPoint& first = fPts[0];
351 const SkPoint& rhFirst = rh.fPts[0];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000352 return first.fY == rhFirst.fY
353 ? first.fX < rhFirst.fX
354 : first.fY < rhFirst.fY;
355 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000356
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000357 SkPoint fPts[4];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000358 int fID; // id of edge generating data
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000359 uint8_t fVerb; // FIXME: not read from everywhere
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000360 bool fCloseCall; // edge is trimmable if not originally coincident
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000361};
362
caryclark@google.com6680fb12012-02-07 22:10:51 +0000363class OutEdgeBuilder {
364public:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000365 OutEdgeBuilder(bool fill)
366 : fFill(fill) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000367 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000368
caryclark@google.coma5764232012-03-28 16:20:21 +0000369 void addCurve(const SkPoint line[4], SkPath::Verb verb, int id,
370 bool closeCall) {
caryclark@google.comc17972e2012-02-20 21:33:22 +0000371 OutEdge& newEdge = fEdges.push_back();
caryclark@google.coma5764232012-03-28 16:20:21 +0000372 memcpy(newEdge.fPts, line, (verb + 1) * sizeof(SkPoint));
373 newEdge.fVerb = verb;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000374 newEdge.fID = id;
375 newEdge.fCloseCall = closeCall;
376 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000377
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000378 bool trimLine(SkScalar y, int id) {
379 size_t count = fEdges.count();
380 while (count-- != 0) {
381 OutEdge& edge = fEdges[count];
382 if (edge.fID != id) {
383 continue;
384 }
385 if (edge.fCloseCall) {
386 return false;
387 }
388 SkASSERT(edge.fPts[0].fY <= y);
389 if (edge.fPts[1].fY <= y) {
390 continue;
391 }
392 edge.fPts[1].fX = edge.fPts[0].fX + (y - edge.fPts[0].fY)
393 * (edge.fPts[1].fX - edge.fPts[0].fX)
394 / (edge.fPts[1].fY - edge.fPts[0].fY);
395 edge.fPts[1].fY = y;
caryclark@google.com78e17132012-04-17 11:40:34 +0000396#if DEBUG_TRIM_LINE
397 SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id,
398 edge.fPts[1].fX, y);
399#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000400 return true;
401 }
402 return false;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000403 }
404
405 void assemble(SkPath& simple) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000406 size_t listCount = fEdges.count();
407 if (listCount == 0) {
408 return;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000409 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000410 do {
411 size_t listIndex = 0;
412 int advance = 1;
413 while (listIndex < listCount && fTops[listIndex] == 0) {
414 ++listIndex;
415 }
416 if (listIndex >= listCount) {
417 break;
418 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000419 int closeEdgeIndex = -listIndex - 1;
caryclark@google.comfb173422012-04-10 18:28:55 +0000420 // the curve is deferred and not added right away because the
421 // following edge may extend the first curve.
caryclark@google.coma5764232012-03-28 16:20:21 +0000422 SkPoint firstPt, lastCurve[4];
423 uint8_t lastVerb;
caryclark@google.comfb173422012-04-10 18:28:55 +0000424#if DEBUG_ASSEMBLE
425 int firstIndex, lastIndex;
426 const int tab = 8;
427#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +0000428 bool doMove = true;
429 int edgeIndex;
430 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000431 SkPoint* ptArray = fEdges[listIndex].fPts;
432 uint8_t verb = fEdges[listIndex].fVerb;
caryclark@google.comfb173422012-04-10 18:28:55 +0000433 SkPoint* curve[4];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000434 if (advance < 0) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000435 curve[0] = &ptArray[verb];
436 if (verb == SkPath::kCubic_Verb) {
437 curve[1] = &ptArray[2];
438 curve[2] = &ptArray[1];
439 }
440 curve[verb] = &ptArray[0];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000441 } else {
caryclark@google.comfb173422012-04-10 18:28:55 +0000442 curve[0] = &ptArray[0];
443 if (verb == SkPath::kCubic_Verb) {
444 curve[1] = &ptArray[1];
445 curve[2] = &ptArray[2];
446 }
447 curve[verb] = &ptArray[verb];
448 }
449 if (verb == SkPath::kQuad_Verb) {
450 curve[1] = &ptArray[1];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000451 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000452 if (doMove) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000453 firstPt = *curve[0];
454 simple.moveTo(curve[0]->fX, curve[0]->fY);
455#if DEBUG_ASSEMBLE
456 SkDebugf("%s %d moveTo (%g,%g)\n", __FUNCTION__,
457 listIndex + 1, curve[0]->fX, curve[0]->fY);
458 firstIndex = listIndex;
459#endif
460 for (int index = 0; index <= verb; ++index) {
461 lastCurve[index] = *curve[index];
caryclark@google.coma5764232012-03-28 16:20:21 +0000462 }
caryclark@google.coma5764232012-03-28 16:20:21 +0000463 doMove = false;
464 } else {
caryclark@google.comfb173422012-04-10 18:28:55 +0000465 bool gap = lastCurve[lastVerb] != *curve[0];
466 if (gap || lastVerb != SkPath::kLine_Verb) { // output the accumulated curve before the gap
caryclark@google.coma5764232012-03-28 16:20:21 +0000467 // FIXME: see comment in bridge -- this probably
468 // conceals errors
caryclark@google.comfb173422012-04-10 18:28:55 +0000469 SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY,
470 curve[0]->fY) <= 10);
caryclark@google.coma5764232012-03-28 16:20:21 +0000471 switch (lastVerb) {
472 case SkPath::kLine_Verb:
473 simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
474 break;
475 case SkPath::kQuad_Verb:
476 simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
477 lastCurve[2].fX, lastCurve[2].fY);
478 break;
479 case SkPath::kCubic_Verb:
480 simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
481 lastCurve[2].fX, lastCurve[2].fY,
482 lastCurve[3].fX, lastCurve[3].fY);
483 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000484 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000485#if DEBUG_ASSEMBLE
486 SkDebugf("%*s %d %sTo (%g,%g)\n", tab, "", lastIndex + 1,
487 kLVerbStr[lastVerb], lastCurve[lastVerb].fX,
488 lastCurve[lastVerb].fY);
489#endif
caryclark@google.coma5764232012-03-28 16:20:21 +0000490 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000491 int firstCopy = 1;
caryclark@google.com78e17132012-04-17 11:40:34 +0000492 if (gap || (lastVerb == SkPath::kLine_Verb
493 && (verb != SkPath::kLine_Verb
494 || !extendLine(lastCurve, *curve[verb])))) {
caryclark@google.coma5764232012-03-28 16:20:21 +0000495 // FIXME: see comment in bridge -- this probably
496 // conceals errors
caryclark@google.comfb173422012-04-10 18:28:55 +0000497 SkASSERT(lastCurve[lastVerb] == *curve[0] ||
498 (fFill && UlpsDiff(lastCurve[lastVerb].fY,
499 curve[0]->fY) <= 10));
500 simple.lineTo(curve[0]->fX, curve[0]->fY);
501#if DEBUG_ASSEMBLE
502 SkDebugf("%*s %d gap lineTo (%g,%g)\n", tab, "",
503 lastIndex + 1, curve[0]->fX, curve[0]->fY);
504#endif
505 firstCopy = 0;
506 } else if (lastVerb != SkPath::kLine_Verb) {
507 firstCopy = 0;
caryclark@google.coma5764232012-03-28 16:20:21 +0000508 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000509 for (int index = firstCopy; index <= verb; ++index) {
510 lastCurve[index] = *curve[index];
caryclark@google.coma5764232012-03-28 16:20:21 +0000511 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000512 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000513 lastVerb = verb;
514#if DEBUG_ASSEMBLE
515 lastIndex = listIndex;
516#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +0000517 if (advance < 0) {
518 edgeIndex = fTops[listIndex];
519 fTops[listIndex] = 0;
caryclark@google.comfb173422012-04-10 18:28:55 +0000520 } else {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000521 edgeIndex = fBottoms[listIndex];
522 fBottoms[listIndex] = 0;
523 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000524 if (edgeIndex) {
525 listIndex = abs(edgeIndex) - 1;
526 if (edgeIndex < 0) {
527 fTops[listIndex] = 0;
528 } else {
529 fBottoms[listIndex] = 0;
530 }
531 }
532 if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000533 switch (lastVerb) {
534 case SkPath::kLine_Verb:
535 simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
536 break;
537 case SkPath::kQuad_Verb:
538 simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
539 lastCurve[2].fX, lastCurve[2].fY);
540 break;
541 case SkPath::kCubic_Verb:
542 simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
543 lastCurve[2].fX, lastCurve[2].fY,
544 lastCurve[3].fX, lastCurve[3].fY);
545 break;
546 }
547#if DEBUG_ASSEMBLE
548 SkDebugf("%*s %d %sTo last (%g, %g)\n", tab, "",
549 lastIndex + 1, kLVerbStr[lastVerb],
550 lastCurve[lastVerb].fX, lastCurve[lastVerb].fY);
551#endif
caryclark@google.coma5764232012-03-28 16:20:21 +0000552 if (lastCurve[lastVerb] != firstPt) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000553 simple.lineTo(firstPt.fX, firstPt.fY);
554#if DEBUG_ASSEMBLE
555 SkDebugf("%*s %d final line (%g, %g)\n", tab, "",
556 firstIndex + 1, firstPt.fX, firstPt.fY);
557#endif
caryclark@google.com4917f172012-03-05 22:01:21 +0000558 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000559 simple.close();
caryclark@google.comfb173422012-04-10 18:28:55 +0000560#if DEBUG_ASSEMBLE
561 SkDebugf("%*s close\n", tab, "");
562#endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000563 break;
564 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000565 // if this and next edge go different directions
566#if DEBUG_ASSEMBLE
567 SkDebugf("%*s advance=%d edgeIndex=%d flip=%s\n", tab, "",
568 advance, edgeIndex, advance > 0 ^ edgeIndex < 0 ?
569 "true" : "false");
570#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +0000571 if (advance > 0 ^ edgeIndex < 0) {
572 advance = -advance;
573 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000574 } while (edgeIndex);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000575 } while (true);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000576 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000577
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000578 // sort points by y, then x
579 // if x/y is identical, sort bottoms before tops
580 // if identical and both tops/bottoms, sort by angle
581 static bool lessThan(SkTArray<OutEdge>& edges, const int one,
582 const int two) {
583 const OutEdge& oneEdge = edges[abs(one) - 1];
584 int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
585 const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
586 const OutEdge& twoEdge = edges[abs(two) - 1];
587 int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
588 const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
589 if (startPt1.fY != startPt2.fY) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000590 #if DEBUG_OUT_LESS_THAN
591 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
592 one, two, startPt1.fY, startPt2.fY,
593 startPt1.fY < startPt2.fY ? "true" : "false");
594 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000595 return startPt1.fY < startPt2.fY;
596 }
597 if (startPt1.fX != startPt2.fX) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000598 #if DEBUG_OUT_LESS_THAN
599 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
600 one, two, startPt1.fX, startPt2.fX,
601 startPt1.fX < startPt2.fX ? "true" : "false");
602 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000603 return startPt1.fX < startPt2.fX;
604 }
605 const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
606 const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
607 SkScalar dy1 = startPt1.fY - endPt1.fY;
608 SkScalar dy2 = startPt2.fY - endPt2.fY;
609 SkScalar dy1y2 = dy1 * dy2;
610 if (dy1y2 < 0) { // different signs
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000611 #if DEBUG_OUT_LESS_THAN
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000612 SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
613 dy1 > 0 ? "true" : "false");
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000614 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000615 return dy1 > 0; // one < two if one goes up and two goes down
616 }
617 if (dy1y2 == 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000618 #if DEBUG_OUT_LESS_THAN
619 SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
620 one, two, endPt1.fX < endPt2.fX ? "true" : "false");
621 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000622 return endPt1.fX < endPt2.fX;
623 }
624 SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
625 SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000626 #if DEBUG_OUT_LESS_THAN
627 SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
628 one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
629 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000630 return dy2 > 0 ^ dx1y2 < dx2y1;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000631 }
632
caryclark@google.com6008c6562012-02-15 22:01:16 +0000633 // Sort the indices of paired points and then create more indices so
634 // assemble() can find the next edge and connect the top or bottom
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000635 void bridge() {
636 size_t index;
637 size_t count = fEdges.count();
638 if (!count) {
639 return;
640 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000641 SkASSERT(!fFill || count > 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000642 fTops.setCount(count);
643 sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
644 fBottoms.setCount(count);
645 sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000646 SkTDArray<int> order;
647 for (index = 1; index <= count; ++index) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000648 *order.append() = -index;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000649 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000650 for (index = 1; index <= count; ++index) {
651 *order.append() = index;
652 }
653 QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000654 int* lastPtr = order.end() - 1;
655 int* leftPtr = order.begin();
656 while (leftPtr < lastPtr) {
657 int leftIndex = *leftPtr;
658 int leftOutIndex = abs(leftIndex) - 1;
659 const OutEdge& left = fEdges[leftOutIndex];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000660 int* rightPtr = leftPtr + 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000661 int rightIndex = *rightPtr;
662 int rightOutIndex = abs(rightIndex) - 1;
663 const OutEdge& right = fEdges[rightOutIndex];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000664 bool pairUp = fFill;
665 if (!pairUp) {
666 const SkPoint& leftMatch =
667 left.fPts[leftIndex < 0 ? 0 : left.fVerb];
668 const SkPoint& rightMatch =
669 right.fPts[rightIndex < 0 ? 0 : right.fVerb];
670 pairUp = leftMatch == rightMatch;
671 } else {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000672 #if DEBUG_OUT
caryclark@google.comd88e0892012-03-27 13:23:51 +0000673 // FIXME : not happy that error in low bit is allowed
674 // this probably conceals error elsewhere
675 if (UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
676 right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) > 1) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000677 *fMismatches.append() = leftIndex;
678 if (rightPtr == lastPtr) {
679 *fMismatches.append() = rightIndex;
680 }
681 pairUp = false;
682 }
683 #else
caryclark@google.comd88e0892012-03-27 13:23:51 +0000684 SkASSERT(UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
685 right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) <= 10);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000686 #endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000687 }
688 if (pairUp) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000689 if (leftIndex < 0) {
690 fTops[leftOutIndex] = rightIndex;
691 } else {
692 fBottoms[leftOutIndex] = rightIndex;
693 }
694 if (rightIndex < 0) {
695 fTops[rightOutIndex] = leftIndex;
696 } else {
697 fBottoms[rightOutIndex] = leftIndex;
698 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000699 ++rightPtr;
700 }
701 leftPtr = rightPtr;
702 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000703#if DEBUG_OUT
704 int* mismatch = fMismatches.begin();
705 while (mismatch != fMismatches.end()) {
706 int leftIndex = *mismatch++;
707 int leftOutIndex = abs(leftIndex) - 1;
708 const OutEdge& left = fEdges[leftOutIndex];
709 const SkPoint& leftPt = left.fPts[leftIndex < 0 ? 0 : left.fVerb];
710 SkDebugf("%s left=%d %s (%1.9g,%1.9g)\n",
711 __FUNCTION__, left.fID, leftIndex < 0 ? "top" : "bot",
712 leftPt.fX, leftPt.fY);
713 }
714 SkASSERT(fMismatches.count() == 0);
715#endif
caryclark@google.comfb173422012-04-10 18:28:55 +0000716#if DEBUG_BRIDGE
717 for (index = 0; index < count; ++index) {
718 const OutEdge& edge = fEdges[index];
719 uint8_t verb = edge.fVerb;
720 SkDebugf("%s %d edge=%d %s (%1.9g,%1.9g) (%1.9g,%1.9g)\n",
721 index == 0 ? __FUNCTION__ : " ",
722 index + 1, edge.fID, kLVerbStr[verb], edge.fPts[0].fX,
723 edge.fPts[0].fY, edge.fPts[verb].fX, edge.fPts[verb].fY);
724 }
725 for (index = 0; index < count; ++index) {
726 SkDebugf(" top of % 2d connects to %s of % 2d\n", index + 1,
727 fTops[index] < 0 ? "top " : "bottom", abs(fTops[index]));
728 SkDebugf(" bottom of % 2d connects to %s of % 2d\n", index + 1,
729 fBottoms[index] < 0 ? "top " : "bottom", abs(fBottoms[index]));
730 }
731#endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000732 }
733
caryclark@google.com6008c6562012-02-15 22:01:16 +0000734protected:
caryclark@google.com6680fb12012-02-07 22:10:51 +0000735 SkTArray<OutEdge> fEdges;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000736 SkTDArray<int> fTops;
737 SkTDArray<int> fBottoms;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000738 bool fFill;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000739#if DEBUG_OUT
740 SkTDArray<int> fMismatches;
741#endif
caryclark@google.com6680fb12012-02-07 22:10:51 +0000742};
743
caryclark@google.comc6825902012-02-03 22:07:47 +0000744// Bounds, unlike Rect, does not consider a vertical line to be empty.
745struct Bounds : public SkRect {
746 static bool Intersects(const Bounds& a, const Bounds& b) {
747 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
748 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
749 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000750
caryclark@google.com6008c6562012-02-15 22:01:16 +0000751 bool isEmpty() {
752 return fLeft > fRight || fTop > fBottom
753 || fLeft == fRight && fTop == fBottom
754 || isnan(fLeft) || isnan(fRight)
755 || isnan(fTop) || isnan(fBottom);
756 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000757};
758
caryclark@google.com4917f172012-03-05 22:01:21 +0000759class Intercepts {
760public:
761 Intercepts()
762 : fTopIntercepts(0)
caryclark@google.com198e0542012-03-30 18:47:02 +0000763 , fBottomIntercepts(0)
764 , fExplicit(false) {
765 }
766
767 Intercepts& operator=(const Intercepts& src) {
768 fTs = src.fTs;
769 fTopIntercepts = src.fTopIntercepts;
770 fBottomIntercepts = src.fBottomIntercepts;
771 }
772
773 // OPTIMIZATION: remove this function if it's never called
774 double t(int tIndex) const {
775 if (tIndex == 0) {
776 return 0;
777 }
778 if (tIndex > fTs.count()) {
779 return 1;
780 }
781 return fTs[tIndex - 1];
caryclark@google.com4917f172012-03-05 22:01:21 +0000782 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000783
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000784#if DEBUG_DUMP
caryclark@google.coma5764232012-03-28 16:20:21 +0000785 void dump(const SkPoint* pts, SkPath::Verb verb) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000786 const char className[] = "Intercepts";
787 const int tab = 8;
788 for (int i = 0; i < fTs.count(); ++i) {
789 SkPoint out;
caryclark@google.coma5764232012-03-28 16:20:21 +0000790 switch (verb) {
791 case SkPath::kLine_Verb:
792 LineXYAtT(pts, fTs[i], &out);
793 break;
794 case SkPath::kQuad_Verb:
795 QuadXYAtT(pts, fTs[i], &out);
796 break;
797 case SkPath::kCubic_Verb:
798 CubicXYAtT(pts, fTs[i], &out);
799 break;
800 default:
801 SkASSERT(0);
802 }
caryclark@google.com752b60e2012-03-22 21:11:17 +0000803 SkDebugf("%*s.fTs[%d]=%1.9g (%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000804 className, i, fTs[i], out.fX, out.fY);
805 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000806 SkDebugf("%*s.fTopIntercepts=%u\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000807 className, fTopIntercepts);
caryclark@google.com198e0542012-03-30 18:47:02 +0000808 SkDebugf("%*s.fBottomIntercepts=%u\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000809 className, fBottomIntercepts);
caryclark@google.comfb173422012-04-10 18:28:55 +0000810 SkDebugf("%*s.fExplicit=%d\n", tab + sizeof(className),
811 className, fExplicit);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000812 }
813#endif
814
caryclark@google.comc6825902012-02-03 22:07:47 +0000815 SkTDArray<double> fTs;
caryclark@google.com198e0542012-03-30 18:47:02 +0000816 unsigned char fTopIntercepts; // 0=init state 1=1 edge >1=multiple edges
817 unsigned char fBottomIntercepts;
818 bool fExplicit; // if set, suppress 0 and 1
819
caryclark@google.comc6825902012-02-03 22:07:47 +0000820};
821
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000822struct HorizontalEdge {
823 bool operator<(const HorizontalEdge& rh) const {
824 return fY == rh.fY ? fLeft == rh.fLeft ? fRight < rh.fRight
825 : fLeft < rh.fLeft : fY < rh.fY;
826 }
827
828#if DEBUG_DUMP
829 void dump() {
830 const char className[] = "HorizontalEdge";
831 const int tab = 4;
caryclark@google.com752b60e2012-03-22 21:11:17 +0000832 SkDebugf("%*s.fLeft=%1.9g\n", tab + sizeof(className), className, fLeft);
833 SkDebugf("%*s.fRight=%1.9g\n", tab + sizeof(className), className, fRight);
834 SkDebugf("%*s.fY=%1.9g\n", tab + sizeof(className), className, fY);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000835 }
836#endif
837
838 SkScalar fLeft;
839 SkScalar fRight;
840 SkScalar fY;
841};
842
caryclark@google.com6680fb12012-02-07 22:10:51 +0000843struct InEdge {
844 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000845 return fBounds.fTop == rh.fBounds.fTop
846 ? fBounds.fLeft < rh.fBounds.fLeft
847 : fBounds.fTop < rh.fBounds.fTop;
848 }
849
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000850 // Avoid collapsing t values that are close to the same since
851 // we walk ts to describe consecutive intersections. Since a pair of ts can
852 // be nearly equal, any problems caused by this should be taken care
853 // of later.
854 int add(double* ts, size_t count, ptrdiff_t verbIndex) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000855 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
caryclark@google.com6008c6562012-02-15 22:01:16 +0000856 bool foundIntercept = false;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000857 int insertedAt = -1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000858 Intercepts& intercepts = fIntercepts[verbIndex];
caryclark@google.comc6825902012-02-03 22:07:47 +0000859 for (size_t index = 0; index < count; ++index) {
860 double t = ts[index];
caryclark@google.com4917f172012-03-05 22:01:21 +0000861 if (t <= 0) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000862 intercepts.fTopIntercepts <<= 1;
caryclark@google.com4917f172012-03-05 22:01:21 +0000863 fContainsIntercepts |= ++intercepts.fTopIntercepts > 1;
864 continue;
865 }
866 if (t >= 1) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000867 intercepts.fBottomIntercepts <<= 1;
caryclark@google.com4917f172012-03-05 22:01:21 +0000868 fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000869 continue;
870 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000871 fIntersected = true;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000872 foundIntercept = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000873 size_t tCount = intercepts.fTs.count();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000874 double delta;
caryclark@google.comc6825902012-02-03 22:07:47 +0000875 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
876 if (t <= intercepts.fTs[idx2]) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000877 // FIXME: ? if (t < intercepts.fTs[idx2]) // failed
878 delta = intercepts.fTs[idx2] - t;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000879 if (delta > 0) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000880 insertedAt = idx2;
caryclark@google.comc6825902012-02-03 22:07:47 +0000881 *intercepts.fTs.insert(idx2) = t;
caryclark@google.comc6825902012-02-03 22:07:47 +0000882 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000883 goto nextPt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000884 }
885 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000886 if (tCount == 0 || (delta = t - intercepts.fTs[tCount - 1]) > 0) {
887 insertedAt = tCount;
caryclark@google.comc6825902012-02-03 22:07:47 +0000888 *intercepts.fTs.append() = t;
889 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000890 nextPt:
891 ;
caryclark@google.comc6825902012-02-03 22:07:47 +0000892 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000893 fContainsIntercepts |= foundIntercept;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000894 return insertedAt;
caryclark@google.comc6825902012-02-03 22:07:47 +0000895 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000896
caryclark@google.comfb173422012-04-10 18:28:55 +0000897 void addPartial(SkTArray<InEdge>& edges, int ptStart, int ptEnd,
caryclark@google.com198e0542012-03-30 18:47:02 +0000898 int verbStart, int verbEnd) {
899 InEdge* edge = edges.push_back_n(1);
900 int verbCount = verbEnd - verbStart;
901 edge->fIntercepts.push_back_n(verbCount);
902 uint8_t* verbs = &fVerbs[verbStart];
903 for (int ceptIdx = 0; ceptIdx < verbCount; ++ceptIdx) {
904 edge->fIntercepts[ceptIdx] = fIntercepts[verbStart + ceptIdx];
905 }
906 edge->fPts.append(ptEnd - ptStart, &fPts[ptStart]);
907 edge->fVerbs.append(verbCount, &fVerbs[verbStart]);
908 edge->setBounds();
caryclark@google.com198e0542012-03-30 18:47:02 +0000909 edge->fWinding = fWinding;
910 edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
911 }
912
caryclark@google.comfb173422012-04-10 18:28:55 +0000913 void addSplit(SkTArray<InEdge>& edges, SkPoint* pts, uint8_t verb,
914 Intercepts& intercepts, int firstT, int lastT, bool flipped) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000915 InEdge* edge = edges.push_back_n(1);
916 edge->fIntercepts.push_back_n(1);
caryclark@google.comfb173422012-04-10 18:28:55 +0000917 if (firstT == 0) {
918 *edge->fIntercepts[0].fTs.append() = 0;
919 } else {
920 *edge->fIntercepts[0].fTs.append() = intercepts.fTs[firstT - 1];
921 }
922 bool add1 = lastT == intercepts.fTs.count();
923 edge->fIntercepts[0].fTs.append(lastT - firstT, &intercepts.fTs[firstT]);
924 if (add1) {
925 *edge->fIntercepts[0].fTs.append() = 1;
926 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000927 edge->fIntercepts[0].fExplicit = true;
caryclark@google.comfb173422012-04-10 18:28:55 +0000928 edge->fPts.append(verb + 1, pts);
caryclark@google.com198e0542012-03-30 18:47:02 +0000929 edge->fVerbs.append(1, &verb);
caryclark@google.comfb173422012-04-10 18:28:55 +0000930 // FIXME: bounds could be better for partial Ts
931 edge->setSubBounds();
caryclark@google.com198e0542012-03-30 18:47:02 +0000932 edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
933 if (flipped) {
caryclark@google.comfb173422012-04-10 18:28:55 +0000934 edge->flipTs();
935 edge->fWinding = -fWinding;
936 } else {
937 edge->fWinding = fWinding;
caryclark@google.com198e0542012-03-30 18:47:02 +0000938 }
939 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000940
caryclark@google.com6680fb12012-02-07 22:10:51 +0000941 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000942 // FIXME: in the pathological case where there is a ton of edges, binary search?
943 size_t count = fCached.count();
944 for (size_t index = 0; index < count; ++index) {
945 if (edge == fCached[index]) {
946 return true;
947 }
948 if (edge < fCached[index]) {
949 *fCached.insert(index) = edge;
950 return false;
951 }
952 }
953 *fCached.append() = edge;
954 return false;
955 }
956
caryclark@google.comfb173422012-04-10 18:28:55 +0000957 void complete(signed char winding) {
caryclark@google.com198e0542012-03-30 18:47:02 +0000958 setBounds();
959 fIntercepts.push_back_n(fVerbs.count());
960 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
961 flip();
962 }
963 fContainsIntercepts = fIntersected = false;
caryclark@google.com198e0542012-03-30 18:47:02 +0000964 }
965
caryclark@google.comfb173422012-04-10 18:28:55 +0000966 void flip() {
caryclark@google.com198e0542012-03-30 18:47:02 +0000967 size_t index;
968 size_t last = fPts.count() - 1;
969 for (index = 0; index < last; ++index, --last) {
970 SkTSwap<SkPoint>(fPts[index], fPts[last]);
971 }
972 last = fVerbs.count() - 1;
973 for (index = 0; index < last; ++index, --last) {
974 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
975 }
976 }
caryclark@google.comfb173422012-04-10 18:28:55 +0000977
978 void flipTs() {
979 SkASSERT(fIntercepts.count() == 1);
980 Intercepts& intercepts = fIntercepts[0];
981 SkASSERT(intercepts.fExplicit);
982 SkTDArray<double>& ts = intercepts.fTs;
983 size_t index;
984 size_t last = ts.count() - 1;
985 for (index = 0; index < last; ++index, --last) {
986 SkTSwap<double>(ts[index], ts[last]);
987 }
988 }
caryclark@google.com198e0542012-03-30 18:47:02 +0000989
990 void reset() {
991 fCached.reset();
992 fIntercepts.reset();
993 fPts.reset();
994 fVerbs.reset();
995 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com198e0542012-03-30 18:47:02 +0000996 fWinding = 0;
997 fContainsIntercepts = false;
998 fIntersected = false;
999 }
1000
1001 void setBounds() {
caryclark@google.comc6825902012-02-03 22:07:47 +00001002 SkPoint* ptPtr = fPts.begin();
1003 SkPoint* ptLast = fPts.end();
1004 if (ptPtr == ptLast) {
caryclark@google.com198e0542012-03-30 18:47:02 +00001005 SkDebugf("%s empty edge\n", __FUNCTION__);
caryclark@google.comc6825902012-02-03 22:07:47 +00001006 SkASSERT(0);
1007 // FIXME: delete empty edge?
1008 return;
1009 }
1010 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
1011 ++ptPtr;
1012 while (ptPtr != ptLast) {
1013 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
1014 ++ptPtr;
1015 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001016 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001017
1018 // recompute bounds based on subrange of T values
1019 void setSubBounds() {
1020 SkASSERT(fIntercepts.count() == 1);
1021 Intercepts& intercepts = fIntercepts[0];
1022 SkASSERT(intercepts.fExplicit);
1023 SkASSERT(fVerbs.count() == 1);
1024 SkTDArray<double>& ts = intercepts.fTs;
1025 if (fVerbs[0] == SkPath::kQuad_Verb) {
1026 SkASSERT(fPts.count() == 3);
1027 QuadSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
1028 } else {
1029 SkASSERT(fVerbs[0] == SkPath::kCubic_Verb);
1030 SkASSERT(fPts.count() == 4);
1031 CubicSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
1032 }
1033 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001034
caryclark@google.comfb173422012-04-10 18:28:55 +00001035 void splitInflectionPts(SkTArray<InEdge>& edges) {
caryclark@google.com198e0542012-03-30 18:47:02 +00001036 if (!fIntersected) {
1037 return;
1038 }
1039 uint8_t* verbs = fVerbs.begin();
1040 SkPoint* pts = fPts.begin();
1041 int lastVerb = 0;
1042 int lastPt = 0;
1043 uint8_t verb;
1044 bool edgeSplit = false;
1045 for (int ceptIdx = 0; ceptIdx < fIntercepts.count(); ++ceptIdx, pts += verb) {
1046 Intercepts& intercepts = fIntercepts[ceptIdx];
1047 verb = *verbs++;
1048 if (verb <= SkPath::kLine_Verb) {
1049 continue;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001050 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001051 size_t tCount = intercepts.fTs.count();
1052 if (!tCount) {
1053 continue;
1054 }
1055 size_t tIndex = -1;
1056 SkScalar y = pts[0].fY;
1057 int lastSplit = 0;
1058 int firstSplit = -1;
1059 bool curveSplit = false;
1060 while (++tIndex < tCount) {
1061 double nextT = intercepts.fTs[tIndex];
1062 SkScalar nextY = verb == SkPath::kQuad_Verb
1063 ? QuadYAtT(pts, nextT) : CubicYAtT(pts, nextT);
1064 if (nextY < y) {
1065 edgeSplit = curveSplit = true;
1066 if (firstSplit < 0) {
1067 firstSplit = tIndex;
1068 int nextPt = pts - fPts.begin();
1069 int nextVerb = verbs - 1 - fVerbs.begin();
1070 if (lastVerb < nextVerb) {
caryclark@google.comfb173422012-04-10 18:28:55 +00001071 addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
caryclark@google.com198e0542012-03-30 18:47:02 +00001072 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001073 SkDebugf("%s addPartial 1\n", __FUNCTION__);
caryclark@google.com198e0542012-03-30 18:47:02 +00001074 #endif
1075 }
1076 lastPt = nextPt;
1077 lastVerb = nextVerb;
1078 }
1079 } else {
1080 if (firstSplit >= 0) {
1081 if (lastSplit < firstSplit) {
caryclark@google.comfb173422012-04-10 18:28:55 +00001082 addSplit(edges, pts, verb, intercepts,
1083 lastSplit, firstSplit, false);
caryclark@google.com198e0542012-03-30 18:47:02 +00001084 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001085 SkDebugf("%s addSplit 1 tIndex=%d,%d\n",
1086 __FUNCTION__, lastSplit, firstSplit);
caryclark@google.com198e0542012-03-30 18:47:02 +00001087 #endif
1088 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001089 addSplit(edges, pts, verb, intercepts,
1090 firstSplit, tIndex, true);
caryclark@google.com198e0542012-03-30 18:47:02 +00001091 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001092 SkDebugf("%s addSplit 2 tIndex=%d,%d flip\n",
1093 __FUNCTION__, firstSplit, tIndex);
caryclark@google.com198e0542012-03-30 18:47:02 +00001094 #endif
1095 lastSplit = tIndex;
1096 firstSplit = -1;
1097 }
1098 }
1099 y = nextY;
1100 }
1101 if (curveSplit) {
1102 if (firstSplit < 0) {
1103 firstSplit = lastSplit;
1104 } else {
caryclark@google.comfb173422012-04-10 18:28:55 +00001105 addSplit(edges, pts, verb, intercepts, lastSplit,
1106 firstSplit, false);
caryclark@google.com198e0542012-03-30 18:47:02 +00001107 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001108 SkDebugf("%s addSplit 3 tIndex=%d,%d\n", __FUNCTION__,
1109 lastSplit, firstSplit);
caryclark@google.com198e0542012-03-30 18:47:02 +00001110 #endif
1111 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001112 addSplit(edges, pts, verb, intercepts, firstSplit,
1113 tIndex, pts[verb].fY < y);
caryclark@google.com198e0542012-03-30 18:47:02 +00001114 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001115 SkDebugf("%s addSplit 4 tIndex=%d,%d %s\n", __FUNCTION__,
1116 firstSplit, tIndex, pts[verb].fY < y ? "flip" : "");
caryclark@google.com198e0542012-03-30 18:47:02 +00001117 #endif
caryclark@google.com6680fb12012-02-07 22:10:51 +00001118 }
1119 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001120 // collapse remainder -- if there's nothing left, clear it somehow?
1121 if (edgeSplit) {
1122 int nextVerb = verbs - 1 - fVerbs.begin();
1123 if (lastVerb < nextVerb) {
1124 int nextPt = pts - fPts.begin();
caryclark@google.comfb173422012-04-10 18:28:55 +00001125 addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
caryclark@google.com198e0542012-03-30 18:47:02 +00001126 #if DEBUG_SPLIT
caryclark@google.comfb173422012-04-10 18:28:55 +00001127 SkDebugf("%s addPartial 2\n", __FUNCTION__);
caryclark@google.com198e0542012-03-30 18:47:02 +00001128 #endif
1129 }
1130 // OPTIMIZATION: reuse the edge instead of marking it empty
1131 reset();
1132 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001133 }
1134
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001135#if DEBUG_DUMP
1136 void dump() {
1137 int i;
1138 const char className[] = "InEdge";
1139 const int tab = 4;
1140 SkDebugf("InEdge %p (edge=%d)\n", this, fID);
1141 for (i = 0; i < fCached.count(); ++i) {
1142 SkDebugf("%*s.fCached[%d]=0x%08x\n", tab + sizeof(className),
1143 className, i, fCached[i]);
1144 }
1145 uint8_t* verbs = fVerbs.begin();
1146 SkPoint* pts = fPts.begin();
1147 for (i = 0; i < fIntercepts.count(); ++i) {
1148 SkDebugf("%*s.fIntercepts[%d]:\n", tab + sizeof(className),
1149 className, i);
caryclark@google.coma5764232012-03-28 16:20:21 +00001150 fIntercepts[i].dump(pts, (SkPath::Verb) *verbs);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001151 pts += *verbs++;
1152 }
1153 for (i = 0; i < fPts.count(); ++i) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001154 SkDebugf("%*s.fPts[%d]=(%1.9g,%1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001155 className, i, fPts[i].fX, fPts[i].fY);
1156 }
1157 for (i = 0; i < fVerbs.count(); ++i) {
1158 SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className),
1159 className, i, fVerbs[i]);
1160 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001161 SkDebugf("%*s.fBounds=(%1.9g, %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001162 className, fBounds.fLeft, fBounds.fTop,
1163 fBounds.fRight, fBounds.fBottom);
1164 SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className,
1165 fWinding);
1166 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
1167 className, fContainsIntercepts);
caryclark@google.com198e0542012-03-30 18:47:02 +00001168 SkDebugf("%*s.fIntersected=%d\n", tab + sizeof(className),
1169 className, fIntersected);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001170 }
1171#endif
1172
caryclark@google.com198e0542012-03-30 18:47:02 +00001173 // FIXME: temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +00001174 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +00001175 SkTArray<Intercepts> fIntercepts; // one per verb
caryclark@google.com4917f172012-03-05 22:01:21 +00001176
caryclark@google.comc6825902012-02-03 22:07:47 +00001177 // persistent data
1178 SkTDArray<SkPoint> fPts;
1179 SkTDArray<uint8_t> fVerbs;
1180 Bounds fBounds;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001181 int fID;
caryclark@google.comc6825902012-02-03 22:07:47 +00001182 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001183 bool fContainsIntercepts;
caryclark@google.com198e0542012-03-30 18:47:02 +00001184 bool fIntersected;
caryclark@google.comc6825902012-02-03 22:07:47 +00001185};
1186
caryclark@google.com6680fb12012-02-07 22:10:51 +00001187class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +00001188public:
1189
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001190InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges,
1191 SkTDArray<HorizontalEdge>& horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +00001192 : fPath(path)
1193 , fCurrentEdge(NULL)
1194 , fEdges(edges)
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001195 , fHorizontalEdges(horizontalEdges)
caryclark@google.comc6825902012-02-03 22:07:47 +00001196 , fIgnoreHorizontal(ignoreHorizontal)
caryclark@google.com198e0542012-03-30 18:47:02 +00001197 , fContainsCurves(false)
caryclark@google.comc6825902012-02-03 22:07:47 +00001198{
1199 walk();
1200}
1201
caryclark@google.com198e0542012-03-30 18:47:02 +00001202bool containsCurves() const {
1203 return fContainsCurves;
1204}
1205
caryclark@google.comc6825902012-02-03 22:07:47 +00001206protected:
1207
1208void addEdge() {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001209 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +00001210 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
1211 fPtOffset = 1;
1212 *fCurrentEdge->fVerbs.append() = fVerb;
1213}
1214
caryclark@google.com6008c6562012-02-15 22:01:16 +00001215bool complete() {
1216 if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
caryclark@google.comfb173422012-04-10 18:28:55 +00001217 fCurrentEdge->complete(fWinding);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001218 fCurrentEdge = NULL;
1219 return true;
1220 }
1221 return false;
1222}
1223
caryclark@google.com78e17132012-04-17 11:40:34 +00001224int direction(SkPath::Verb verb) {
1225 fPtCount = verb + 1;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001226 if (fIgnoreHorizontal && isHorizontal()) {
caryclark@google.comc6825902012-02-03 22:07:47 +00001227 return 0;
1228 }
caryclark@google.com78e17132012-04-17 11:40:34 +00001229 return fPts[0].fY == fPts[verb].fY
1230 ? fPts[0].fX == fPts[verb].fX ? 0 : fPts[0].fX < fPts[verb].fX
1231 ? 1 : -1 : fPts[0].fY < fPts[verb].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +00001232}
1233
1234bool isHorizontal() {
1235 SkScalar y = fPts[0].fY;
1236 for (int i = 1; i < fPtCount; ++i) {
1237 if (fPts[i].fY != y) {
1238 return false;
1239 }
1240 }
1241 return true;
1242}
1243
1244void startEdge() {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001245 if (!fCurrentEdge) {
1246 fCurrentEdge = fEdges.push_back_n(1);
1247 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001248 fWinding = 0;
1249 fPtOffset = 0;
1250}
1251
1252void walk() {
1253 SkPath::Iter iter(fPath, true);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001254 int winding = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +00001255 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
1256 switch (fVerb) {
1257 case SkPath::kMove_Verb:
caryclark@google.comc6825902012-02-03 22:07:47 +00001258 startEdge();
1259 continue;
1260 case SkPath::kLine_Verb:
caryclark@google.com78e17132012-04-17 11:40:34 +00001261 winding = direction(SkPath::kLine_Verb);
caryclark@google.comc6825902012-02-03 22:07:47 +00001262 break;
1263 case SkPath::kQuad_Verb:
caryclark@google.com78e17132012-04-17 11:40:34 +00001264 fVerb = QuadReduceOrder(fPts);
1265 winding = direction(fVerb);
1266 fContainsCurves |= fVerb == SkPath::kQuad_Verb;
caryclark@google.comc6825902012-02-03 22:07:47 +00001267 break;
1268 case SkPath::kCubic_Verb:
caryclark@google.com78e17132012-04-17 11:40:34 +00001269 fVerb = CubicReduceOrder(fPts);
1270 winding = direction(fVerb);
1271 fContainsCurves |= fVerb >= SkPath::kQuad_Verb;
caryclark@google.comc6825902012-02-03 22:07:47 +00001272 break;
1273 case SkPath::kClose_Verb:
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001274 SkASSERT(fCurrentEdge);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001275 complete();
caryclark@google.comc6825902012-02-03 22:07:47 +00001276 continue;
1277 default:
1278 SkDEBUGFAIL("bad verb");
1279 return;
1280 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001281 if (winding == 0) {
1282 HorizontalEdge* horizontalEdge = fHorizontalEdges.append();
1283 // FIXME: for degenerate quads and cubics, compute x extremes
1284 horizontalEdge->fLeft = fPts[0].fX;
1285 horizontalEdge->fRight = fPts[fVerb].fX;
1286 horizontalEdge->fY = fPts[0].fY;
1287 if (horizontalEdge->fLeft > horizontalEdge->fRight) {
1288 SkTSwap<SkScalar>(horizontalEdge->fLeft, horizontalEdge->fRight);
1289 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001290 if (complete()) {
1291 startEdge();
1292 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001293 continue;
1294 }
1295 if (fWinding + winding == 0) {
1296 // FIXME: if prior verb or this verb is a horizontal line, reverse
1297 // it instead of starting a new edge
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001298 SkASSERT(fCurrentEdge);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001299 if (complete()) {
1300 startEdge();
1301 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001302 }
1303 fWinding = winding;
1304 addEdge();
1305 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001306 if (!complete()) {
1307 if (fCurrentEdge) {
1308 fEdges.pop_back();
1309 }
1310 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001311}
1312
1313private:
1314 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001315 InEdge* fCurrentEdge;
1316 SkTArray<InEdge>& fEdges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001317 SkTDArray<HorizontalEdge>& fHorizontalEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +00001318 SkPoint fPts[4];
1319 SkPath::Verb fVerb;
1320 int fPtCount;
1321 int fPtOffset;
1322 int8_t fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +00001323 bool fIgnoreHorizontal;
caryclark@google.com198e0542012-03-30 18:47:02 +00001324 bool fContainsCurves;
caryclark@google.comc6825902012-02-03 22:07:47 +00001325};
1326
caryclark@google.com6680fb12012-02-07 22:10:51 +00001327struct WorkEdge {
1328 SkScalar bottom() const {
1329 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +00001330 }
1331
caryclark@google.com6680fb12012-02-07 22:10:51 +00001332 void init(const InEdge* edge) {
1333 fEdge = edge;
1334 fPts = edge->fPts.begin();
1335 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00001336 }
1337
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001338 bool advance() {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001339 SkASSERT(fVerb < fEdge->fVerbs.end());
1340 fPts += *fVerb++;
1341 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +00001342 }
caryclark@google.com78e17132012-04-17 11:40:34 +00001343
1344 const SkPoint* lastPoints() const {
1345 SkASSERT(fPts >= fEdge->fPts.begin() + lastVerb());
1346 return &fPts[-lastVerb()];
1347 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001348
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001349 SkPath::Verb lastVerb() const {
1350 SkASSERT(fVerb > fEdge->fVerbs.begin());
1351 return (SkPath::Verb) fVerb[-1];
1352 }
1353
caryclark@google.com78e17132012-04-17 11:40:34 +00001354 const SkPoint* points() const {
1355 return fPts;
1356 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001357
1358 SkPath::Verb verb() const {
1359 return (SkPath::Verb) *fVerb;
1360 }
1361
caryclark@google.com6008c6562012-02-15 22:01:16 +00001362 ptrdiff_t verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001363 return fVerb - fEdge->fVerbs.begin();
1364 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001365
caryclark@google.com6680fb12012-02-07 22:10:51 +00001366 int winding() const {
1367 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +00001368 }
1369
caryclark@google.com6680fb12012-02-07 22:10:51 +00001370 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +00001371 const SkPoint* fPts;
1372 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +00001373};
1374
caryclark@google.com6680fb12012-02-07 22:10:51 +00001375// always constructed with SkTDArray because new edges are inserted
1376// this may be a inappropriate optimization, suggesting that a separate array of
1377// ActiveEdge* may be faster to insert and search
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001378
1379// OPTIMIZATION: Brian suggests that global sorting should be unnecessary, since
1380// as active edges are introduced, only local sorting should be required
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001381class ActiveEdge {
1382public:
caryclark@google.com78e17132012-04-17 11:40:34 +00001383 // this logic must be kept in sync with tooCloseToCall
1384 // callers expect this to only read fAbove, fBelow
caryclark@google.com6008c6562012-02-15 22:01:16 +00001385 bool operator<(const ActiveEdge& rh) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001386 double topD = fAbove.fX - rh.fAbove.fX;
1387 if (rh.fAbove.fY < fAbove.fY) {
1388 topD = (rh.fBelow.fY - rh.fAbove.fY) * topD
1389 - (fAbove.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
1390 } else if (rh.fAbove.fY > fAbove.fY) {
1391 topD = (fBelow.fY - fAbove.fY) * topD
1392 + (rh.fAbove.fY - fAbove.fY) * (fBelow.fX - fAbove.fX);
1393 }
1394 double botD = fBelow.fX - rh.fBelow.fX;
1395 if (rh.fBelow.fY > fBelow.fY) {
1396 botD = (rh.fBelow.fY - rh.fAbove.fY) * botD
1397 - (fBelow.fY - rh.fBelow.fY) * (rh.fBelow.fX - rh.fAbove.fX);
1398 } else if (rh.fBelow.fY < fBelow.fY) {
1399 botD = (fBelow.fY - fAbove.fY) * botD
1400 + (rh.fBelow.fY - fBelow.fY) * (fBelow.fX - fAbove.fX);
1401 }
1402 // return sign of greater absolute value
1403 return (fabs(topD) > fabs(botD) ? topD : botD) < 0;
1404 }
1405
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001406 // If a pair of edges are nearly coincident for some span, add a T in the
1407 // edge so it can be shortened to match the other edge. Note that another
1408 // approach is to trim the edge after it is added to the OutBuilder list --
1409 // FIXME: since this has no effect if the edge is already done (i.e.,
1410 // fYBottom >= y) maybe this can only be done by calling trimLine later.
1411 void addTatYBelow(SkScalar y) {
1412 if (fBelow.fY <= y || fYBottom >= y) {
1413 return;
1414 }
1415 addTatYInner(y);
1416 fFixBelow = true;
1417 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001418
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001419 void addTatYAbove(SkScalar y) {
1420 if (fBelow.fY <= y) {
1421 return;
1422 }
1423 addTatYInner(y);
1424 }
1425
1426 void addTatYInner(SkScalar y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00001427 if (fWorkEdge.fPts[0].fY > y) {
1428 backup(y);
1429 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001430 SkScalar left = fWorkEdge.fPts[0].fX;
1431 SkScalar right = fWorkEdge.fPts[1].fX;
1432 if (left > right) {
1433 SkTSwap(left, right);
1434 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001435 double ts[2];
caryclark@google.com78e17132012-04-17 11:40:34 +00001436 SkASSERT(fWorkEdge.fVerb[0] == SkPath::kLine_Verb);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001437 int pts = LineIntersect(fWorkEdge.fPts, left, right, y, ts);
1438 SkASSERT(pts == 1);
1439 // An ActiveEdge or WorkEdge has no need to modify the T values computed
1440 // in the InEdge, except in the following case. If a pair of edges are
1441 // nearly coincident, this may not be detected when the edges are
1442 // intersected. Later, when sorted, and this near-coincidence is found,
1443 // an additional t value must be added, requiring the cast below.
1444 InEdge* writable = const_cast<InEdge*>(fWorkEdge.fEdge);
1445 int insertedAt = writable->add(ts, pts, fWorkEdge.verbIndex());
caryclark@google.com752b60e2012-03-22 21:11:17 +00001446 #if DEBUG_ADJUST_COINCIDENT
1447 SkDebugf("%s edge=%d y=%1.9g t=%1.9g\n", __FUNCTION__, ID(), y, ts[0]);
1448 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001449 if (insertedAt >= 0) {
1450 if (insertedAt + 1 < fTIndex) {
1451 SkASSERT(insertedAt + 2 == fTIndex);
1452 --fTIndex;
1453 }
1454 }
1455 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001456
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001457 bool advanceT() {
caryclark@google.com198e0542012-03-30 18:47:02 +00001458 SkASSERT(fTIndex <= fTs->count() - fExplicitTs);
1459 return ++fTIndex <= fTs->count() - fExplicitTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001460 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001461
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001462 bool advance() {
1463 // FIXME: flip sense of next
1464 bool result = fWorkEdge.advance();
1465 fDone = !result;
1466 initT();
1467 return result;
1468 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001469
1470 void backup(SkScalar y) {
1471 do {
1472 SkASSERT(fWorkEdge.fEdge->fVerbs.begin() < fWorkEdge.fVerb);
1473 fWorkEdge.fPts -= *--fWorkEdge.fVerb;
1474 SkASSERT(fWorkEdge.fEdge->fPts.begin() <= fWorkEdge.fPts);
1475 } while (fWorkEdge.fPts[0].fY >= y);
1476 initT();
caryclark@google.com198e0542012-03-30 18:47:02 +00001477 SkASSERT(!fExplicitTs);
caryclark@google.com752b60e2012-03-22 21:11:17 +00001478 fTIndex = fTs->count() + 1;
1479 }
1480
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001481 void calcLeft(SkScalar y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001482 // OPTIMIZE: put a kDone_Verb at the end of the verb list?
caryclark@google.com4917f172012-03-05 22:01:21 +00001483 if (fDone || fBelow.fY > y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001484 return; // nothing to do; use last
caryclark@google.com4917f172012-03-05 22:01:21 +00001485 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001486 calcLeft();
caryclark@google.com752b60e2012-03-22 21:11:17 +00001487 if (fAbove.fY == fBelow.fY) {
1488 SkDebugf("%s edge=%d fAbove.fY != fBelow.fY %1.9g\n", __FUNCTION__,
1489 ID(), fAbove.fY);
1490 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001491 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001492
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001493 void calcLeft() {
caryclark@google.coma5764232012-03-28 16:20:21 +00001494 void (*xyAtTFunc)(const SkPoint a[], double t, SkPoint* out);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001495 switch (fWorkEdge.verb()) {
caryclark@google.coma5764232012-03-28 16:20:21 +00001496 case SkPath::kLine_Verb:
1497 xyAtTFunc = LineXYAtT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001498 break;
caryclark@google.coma5764232012-03-28 16:20:21 +00001499 case SkPath::kQuad_Verb:
1500 xyAtTFunc = QuadXYAtT;
1501 break;
1502 case SkPath::kCubic_Verb:
1503 xyAtTFunc = CubicXYAtT;
1504 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001505 default:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001506 SkASSERT(0);
1507 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001508 // OPTIMIZATION: if fXAbove, fXBelow have already been computed
1509 // for the fTIndex, don't do it again
1510 // For identical x, this lets us know which edge is first.
1511 // If both edges have T values < 1, check x at next T (fXBelow).
caryclark@google.com198e0542012-03-30 18:47:02 +00001512 int add = (fTIndex <= fTs->count() - fExplicitTs) - 1;
caryclark@google.coma5764232012-03-28 16:20:21 +00001513 double tAbove = t(fTIndex + add);
1514 (*xyAtTFunc)(fWorkEdge.fPts, tAbove, &fAbove);
1515 double tBelow = t(fTIndex - ~add);
1516 (*xyAtTFunc)(fWorkEdge.fPts, tBelow, &fBelow);
1517 SkASSERT(tAbove != tBelow);
1518 // FIXME: this can loop forever
1519 // need a break if we hit the end
caryclark@google.com198e0542012-03-30 18:47:02 +00001520 // FIXME: in unit test, figure out how explicit Ts work as well
caryclark@google.coma5764232012-03-28 16:20:21 +00001521 while (fAbove.fY == fBelow.fY) {
1522 if (add < 0 || fTIndex == fTs->count()) {
1523 add -= 1;
1524 SkASSERT(fTIndex + add >= 0);
1525 tAbove = t(fTIndex + add);
1526 (*xyAtTFunc)(fWorkEdge.fPts, tAbove, &fAbove);
1527 } else {
1528 add += 1;
1529 SkASSERT(fTIndex - ~add <= fTs->count() + 1);
1530 tBelow = t(fTIndex - ~add);
1531 (*xyAtTFunc)(fWorkEdge.fPts, tBelow, &fBelow);
1532 }
1533 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001534 fTAbove = tAbove;
1535 fTBelow = tBelow;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001536 }
1537
caryclark@google.com752b60e2012-03-22 21:11:17 +00001538 bool done(SkScalar bottom) const {
1539 return fDone || fYBottom >= bottom;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001540 }
caryclark@google.com78e17132012-04-17 11:40:34 +00001541
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001542 void fixBelow() {
1543 if (fFixBelow) {
caryclark@google.coma5764232012-03-28 16:20:21 +00001544 switch (fWorkEdge.verb()) {
1545 case SkPath::kLine_Verb:
1546 LineXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1547 break;
1548 case SkPath::kQuad_Verb:
1549 QuadXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1550 break;
1551 case SkPath::kCubic_Verb:
1552 CubicXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
1553 break;
1554 default:
1555 SkASSERT(0);
1556 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001557 fFixBelow = false;
1558 }
1559 }
1560
caryclark@google.com6680fb12012-02-07 22:10:51 +00001561 void init(const InEdge* edge) {
1562 fWorkEdge.init(edge);
1563 initT();
caryclark@google.com4917f172012-03-05 22:01:21 +00001564 fBelow.fY = SK_ScalarMin;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001565 fDone = false;
1566 fYBottom = SK_ScalarMin;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001567 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001568
caryclark@google.com6680fb12012-02-07 22:10:51 +00001569 void initT() {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001570 const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
1571 SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
1572 const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
caryclark@google.com198e0542012-03-30 18:47:02 +00001573 fTs = &interceptPtr->fTs;
1574 fExplicitTs = interceptPtr->fExplicit;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001575 // the above is conceptually the same as
1576 // fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
1577 // but templated arrays don't allow returning a pointer to the end() element
caryclark@google.com6680fb12012-02-07 22:10:51 +00001578 fTIndex = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +00001579 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001580
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001581 // OPTIMIZATION: record if two edges are coincident when the are intersected
1582 // It's unclear how to do this -- seems more complicated than recording the
1583 // t values, since the same t values could exist intersecting non-coincident
1584 // edges.
caryclark@google.com78e17132012-04-17 11:40:34 +00001585 bool isCoincidentWith(const ActiveEdge* edge) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001586 if (fAbove != edge->fAbove || fBelow != edge->fBelow) {
1587 return false;
1588 }
caryclark@google.com78e17132012-04-17 11:40:34 +00001589 SkPath::Verb verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
1590 SkPath::Verb edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb()
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001591 : edge->fWorkEdge.verb();
1592 if (verb != edgeVerb) {
1593 return false;
1594 }
1595 switch (verb) {
caryclark@google.com78e17132012-04-17 11:40:34 +00001596 case SkPath::kLine_Verb:
caryclark@google.com4917f172012-03-05 22:01:21 +00001597 return true;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001598 default:
caryclark@google.com78e17132012-04-17 11:40:34 +00001599 // FIXME: add support for quads, cubics
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001600 SkASSERT(0);
caryclark@google.com78e17132012-04-17 11:40:34 +00001601 return false;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001602 }
1603 return false;
1604 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001605
caryclark@google.com78e17132012-04-17 11:40:34 +00001606 bool isUnordered(const ActiveEdge* edge) const {
1607 return fAbove == edge->fAbove && fBelow == edge->fBelow;
1608 }
1609
1610 SkPath::Verb lastVerb() const {
1611 return fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
1612 }
1613
1614 const SkPoint* lastPoints() const {
1615 return fDone ? fWorkEdge.lastPoints() : fWorkEdge.points();
1616 }
1617
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001618 // The shortest close call edge should be moved into a position where
1619 // it contributes if the winding is transitioning to or from zero.
1620 bool swapClose(const ActiveEdge* next, int prev, int wind, int mask) const {
caryclark@google.comd88e0892012-03-27 13:23:51 +00001621#if DEBUG_ADJUST_COINCIDENT
1622 SkDebugf("%s edge=%d (%g) next=%d (%g) prev=%d wind=%d nextWind=%d\n",
1623 __FUNCTION__, ID(), fBelow.fY, next->ID(), next->fBelow.fY,
1624 prev, wind, wind + next->fWorkEdge.winding());
1625#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001626 if ((prev & mask) == 0 || (wind & mask) == 0) {
1627 return next->fBelow.fY < fBelow.fY;
1628 }
1629 int nextWinding = wind + next->fWorkEdge.winding();
1630 if ((nextWinding & mask) == 0) {
1631 return fBelow.fY < next->fBelow.fY;
1632 }
1633 return false;
1634 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001635
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001636 bool swapCoincident(const ActiveEdge* edge, SkScalar bottom) const {
1637 if (fBelow.fY >= bottom || fDone || edge->fDone) {
1638 return false;
1639 }
1640 ActiveEdge thisWork = *this;
1641 ActiveEdge edgeWork = *edge;
1642 while ((thisWork.advanceT() || thisWork.advance())
1643 && (edgeWork.advanceT() || edgeWork.advance())) {
1644 thisWork.calcLeft();
1645 edgeWork.calcLeft();
1646 if (thisWork < edgeWork) {
1647 return false;
1648 }
1649 if (edgeWork < thisWork) {
1650 return true;
1651 }
1652 }
1653 return false;
1654 }
caryclark@google.com78e17132012-04-17 11:40:34 +00001655
1656 bool swapUnordered(const ActiveEdge* edge, SkScalar bottom) const {
1657 SkASSERT(lastVerb() != SkPath::kLine_Verb
1658 || edge->lastVerb() != SkPath::kLine_Verb);
1659 if (fDone || edge->fDone) {
1660 return false;
1661 }
1662 ActiveEdge thisWork, edgeWork;
1663 extractAboveBelow(thisWork);
1664 edge->extractAboveBelow(edgeWork);
1665 return edgeWork < thisWork;
1666 }
caryclark@google.com752b60e2012-03-22 21:11:17 +00001667
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001668 bool tooCloseToCall(const ActiveEdge* edge) const {
1669 int ulps;
caryclark@google.comd88e0892012-03-27 13:23:51 +00001670 double t1, t2, b1, b2;
caryclark@google.com78e17132012-04-17 11:40:34 +00001671 // This logic must be kept in sync with operator <
caryclark@google.comd88e0892012-03-27 13:23:51 +00001672 if (edge->fAbove.fY < fAbove.fY) {
1673 t1 = (edge->fBelow.fY - edge->fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
1674 t2 = (fAbove.fY - edge->fAbove.fY) * (edge->fBelow.fX - edge->fAbove.fX);
1675 } else if (edge->fAbove.fY > fAbove.fY) {
1676 t1 = (fBelow.fY - fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
1677 t2 = (fAbove.fY - edge->fAbove.fY) * (fBelow.fX - fAbove.fX);
1678 } else {
1679 t1 = fAbove.fX;
1680 t2 = edge->fAbove.fX;
1681 }
1682 if (edge->fBelow.fY > fBelow.fY) {
1683 b1 = (edge->fBelow.fY - edge->fAbove.fY) * (fBelow.fX - edge->fBelow.fX);
1684 b2 = (fBelow.fY - edge->fBelow.fY) * (edge->fBelow.fX - edge->fAbove.fX);
1685 } else if (edge->fBelow.fY < fBelow.fY) {
1686 b1 = (fBelow.fY - fAbove.fY) * (fBelow.fX - edge->fBelow.fX);
1687 b2 = (fBelow.fY - edge->fBelow.fY) * (fBelow.fX - fAbove.fX);
1688 } else {
1689 b1 = fBelow.fX;
1690 b2 = edge->fBelow.fX;
1691 }
1692 if (fabs(t1 - t2) > fabs(b1 - b2)) {
1693 ulps = UlpsDiff(t1, t2);
1694 } else {
1695 ulps = UlpsDiff(b1, b2);
1696 }
1697#if DEBUG_ADJUST_COINCIDENT
1698 SkDebugf("%s this=%d edge=%d ulps=%d\n", __FUNCTION__, ID(), edge->ID(),
1699 ulps);
1700#endif
caryclark@google.com78e17132012-04-17 11:40:34 +00001701 if (ulps < 0 || ulps > 32) {
1702 return false;
1703 }
1704 SkPath::Verb verb = lastVerb();
1705 SkPath::Verb edgeVerb = edge->lastVerb();
1706 if (verb == SkPath::kLine_Verb && edgeVerb == SkPath::kLine_Verb) {
1707 return true;
1708 }
1709 if (verb != SkPath::kLine_Verb && edgeVerb != SkPath::kLine_Verb) {
1710 return false;
1711 }
1712
1713 double ts[2];
1714 bool isLine = true;
1715 bool curveQuad = true;
1716 if (verb == SkPath::kCubic_Verb) {
1717 ts[0] = (fTAbove * 2 + fTBelow) / 3;
1718 ts[1] = (fTAbove + fTBelow * 2) / 3;
1719 curveQuad = isLine = false;
1720 } else if (edgeVerb == SkPath::kCubic_Verb) {
1721 ts[0] = (edge->fTAbove * 2 + edge->fTBelow) / 3;
1722 ts[1] = (edge->fTAbove + edge->fTBelow * 2) / 3;
1723 curveQuad = false;
1724 } else if (verb == SkPath::kQuad_Verb) {
1725 ts[0] = fTAbove;
1726 ts[1] = (fTAbove + fTBelow) / 2;
1727 isLine = false;
1728 } else {
1729 SkASSERT(edgeVerb == SkPath::kQuad_Verb);
1730 ts[0] = edge->fTAbove;
1731 ts[1] = (edge->fTAbove + edge->fTBelow) / 2;
1732 }
1733 const SkPoint* curvePts = isLine ? edge->lastPoints() : lastPoints();
1734 const ActiveEdge* lineEdge = isLine ? this : edge;
1735 SkPoint curveSample[2];
1736 for (int index = 0; index < 2; ++index) {
1737 if (curveQuad) {
1738 QuadXYAtT(curvePts, ts[index], &curveSample[index]);
1739 } else {
1740 CubicXYAtT(curvePts, ts[index], &curveSample[index]);
1741 }
1742 }
1743 return IsCoincident(curveSample, lineEdge->fAbove, lineEdge->fBelow);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001744 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001745
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001746 double nextT() const {
caryclark@google.com198e0542012-03-30 18:47:02 +00001747 SkASSERT(fTIndex <= fTs->count() - fExplicitTs);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001748 return t(fTIndex + 1);
1749 }
caryclark@google.com6680fb12012-02-07 22:10:51 +00001750
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001751 double t() const {
caryclark@google.com198e0542012-03-30 18:47:02 +00001752 return t(fTIndex);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001753 }
1754
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001755 double t(int tIndex) const {
caryclark@google.com198e0542012-03-30 18:47:02 +00001756 if (fExplicitTs) {
1757 SkASSERT(tIndex < fTs->count());
1758 return (*fTs)[tIndex];
1759 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001760 if (tIndex == 0) {
1761 return 0;
1762 }
1763 if (tIndex > fTs->count()) {
1764 return 1;
1765 }
1766 return (*fTs)[tIndex - 1];
1767 }
1768
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001769 // FIXME: debugging only
caryclark@google.com752b60e2012-03-22 21:11:17 +00001770 int ID() const {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001771 return fWorkEdge.fEdge->fID;
1772 }
1773
caryclark@google.com78e17132012-04-17 11:40:34 +00001774private:
1775 // utility used only by swapUnordered
1776 void extractAboveBelow(ActiveEdge& extracted) const {
1777 SkPoint curve[4];
1778 switch (lastVerb()) {
1779 case SkPath::kLine_Verb:
1780 extracted.fAbove = fAbove;
1781 extracted.fBelow = fBelow;
1782 return;
1783 case SkPath::kQuad_Verb:
1784 QuadSubDivide(lastPoints(), fTAbove, fTBelow, curve);
1785 break;
1786 case SkPath::kCubic_Verb:
1787 CubicSubDivide(lastPoints(), fTAbove, fTBelow, curve);
1788 break;
1789 default:
1790 SkASSERT(0);
1791 }
1792 extracted.fAbove = curve[0];
1793 extracted.fBelow = curve[1];
1794 }
1795
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001796public:
caryclark@google.com6680fb12012-02-07 22:10:51 +00001797 WorkEdge fWorkEdge;
1798 const SkTDArray<double>* fTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001799 SkPoint fAbove;
1800 SkPoint fBelow;
caryclark@google.com78e17132012-04-17 11:40:34 +00001801 double fTAbove; // OPTIMIZATION: only required if edge has quads or cubics
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001802 double fTBelow;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001803 SkScalar fYBottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001804 int fCoincident;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001805 int fTIndex;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001806 bool fSkip; // OPTIMIZATION: use bitfields?
1807 bool fCloseCall;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001808 bool fDone;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001809 bool fFixBelow;
caryclark@google.com198e0542012-03-30 18:47:02 +00001810 bool fExplicitTs;
caryclark@google.comc6825902012-02-03 22:07:47 +00001811};
1812
caryclark@google.com6680fb12012-02-07 22:10:51 +00001813static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001814 size_t count = activeEdges.count();
1815 for (size_t index = 0; index < count; ++index) {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001816 if (edge == activeEdges[index].fWorkEdge.fEdge) {
1817 return;
1818 }
1819 }
1820 ActiveEdge* active = activeEdges.append();
1821 active->init(edge);
1822}
1823
caryclark@google.com4917f172012-03-05 22:01:21 +00001824// Find any intersections in the range of active edges. A pair of edges, on
1825// either side of another edge, may change the winding contribution for part of
1826// the edge.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001827// Keep horizontal edges just for
caryclark@google.com4917f172012-03-05 22:01:21 +00001828// the purpose of computing when edges change their winding contribution, since
1829// this is essentially computing the horizontal intersection.
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001830static void addBottomT(InEdge** currentPtr, InEdge** lastPtr,
1831 HorizontalEdge** horizontal) {
1832 InEdge** testPtr = currentPtr - 1;
1833 HorizontalEdge* horzEdge = *horizontal;
1834 SkScalar left = horzEdge->fLeft;
1835 SkScalar bottom = horzEdge->fY;
1836 while (++testPtr != lastPtr) {
1837 InEdge* test = *testPtr;
1838 if (test->fBounds.fBottom <= bottom || test->fBounds.fRight <= left) {
1839 continue;
1840 }
1841 WorkEdge wt;
1842 wt.init(test);
1843 do {
1844 HorizontalEdge** sorted = horizontal;
1845 horzEdge = *sorted;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001846 do {
caryclark@google.com198e0542012-03-30 18:47:02 +00001847 double wtTs[4];
1848 int pts;
1849 uint8_t verb = wt.verb();
1850 switch (verb) {
1851 case SkPath::kLine_Verb:
1852 pts = LineIntersect(wt.fPts, horzEdge->fLeft,
1853 horzEdge->fRight, horzEdge->fY, wtTs);
1854 break;
1855 case SkPath::kQuad_Verb:
1856 pts = QuadIntersect(wt.fPts, horzEdge->fLeft,
1857 horzEdge->fRight, horzEdge->fY, wtTs);
1858 break;
1859 case SkPath::kCubic_Verb:
1860 pts = CubicIntersect(wt.fPts, horzEdge->fLeft,
1861 horzEdge->fRight, horzEdge->fY, wtTs);
1862 break;
1863 }
1864 if (pts) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001865#if DEBUG_ADD_BOTTOM_TS
caryclark@google.com198e0542012-03-30 18:47:02 +00001866 for (int x = 0; x < pts; ++x) {
1867 SkDebugf("%s y=%g wtTs[0]=%g (%g,%g", __FUNCTION__,
1868 horzEdge->fY, wtTs[x], wt.fPts[0].fX, wt.fPts[0].fY);
1869 for (int y = 0; y < verb; ++y) {
1870 SkDebugf(" %g,%g", wt.fPts[y + 1].fX, wt.fPts[y + 1].fY));
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001871 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001872 SkDebugf(")\n");
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001873 }
caryclark@google.com198e0542012-03-30 18:47:02 +00001874 if (pts > verb) {
1875 SkASSERT(0); // FIXME ? should this work?
1876 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1877 }
1878#endif
1879 test->add(wtTs, pts, wt.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001880 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001881 horzEdge = *++sorted;
1882 } while (horzEdge->fY == bottom
1883 && horzEdge->fLeft <= test->fBounds.fRight);
1884 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001885 }
1886}
1887
caryclark@google.coma5764232012-03-28 16:20:21 +00001888static void debugShowLineIntersection(int pts, const WorkEdge& wt,
1889 const WorkEdge& wn, const double wtTs[2], const double wnTs[2]) {
1890#if DEBUG_ADD_INTERSECTING_TS
1891 if (!pts) {
1892 return;
1893 }
1894 SkPoint wtOutPt, wnOutPt;
1895 LineXYAtT(wt.fPts, wtTs[0], &wtOutPt);
1896 LineXYAtT(wn.fPts, wnTs[0], &wnOutPt);
caryclark@google.comfb173422012-04-10 18:28:55 +00001897 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
caryclark@google.coma5764232012-03-28 16:20:21 +00001898 __FUNCTION__,
1899 wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
caryclark@google.comfb173422012-04-10 18:28:55 +00001900 wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY);
caryclark@google.coma5764232012-03-28 16:20:21 +00001901 if (pts == 2) {
1902 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
1903 }
caryclark@google.comfb173422012-04-10 18:28:55 +00001904 SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
caryclark@google.coma5764232012-03-28 16:20:21 +00001905 __FUNCTION__,
1906 wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY,
caryclark@google.comfb173422012-04-10 18:28:55 +00001907 wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY);
caryclark@google.coma5764232012-03-28 16:20:21 +00001908 if (pts == 2) {
1909 SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
1910 }
1911#endif
1912}
1913
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001914static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001915 InEdge** testPtr = currentPtr - 1;
caryclark@google.com752b60e2012-03-22 21:11:17 +00001916 // FIXME: lastPtr should be past the point of interest, so
1917 // test below should be lastPtr - 2
1918 // that breaks testSimplifyTriangle22, so further investigation is needed
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001919 while (++testPtr != lastPtr - 1) {
1920 InEdge* test = *testPtr;
1921 InEdge** nextPtr = testPtr;
1922 do {
1923 InEdge* next = *++nextPtr;
caryclark@google.com198e0542012-03-30 18:47:02 +00001924 // FIXME: this compares against the sentinel sometimes
1925 // OPTIMIZATION: this may never be needed since this gets called
1926 // in two passes now. Verify that double hits are appropriate.
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001927 if (test->cached(next)) {
1928 continue;
1929 }
1930 if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
1931 continue;
1932 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001933 WorkEdge wt, wn;
1934 wt.init(test);
1935 wn.init(next);
1936 do {
caryclark@google.coma5764232012-03-28 16:20:21 +00001937 int pts;
1938 Intersections ts;
1939 bool swap = false;
1940 switch (wt.verb()) {
1941 case SkPath::kLine_Verb:
1942 switch (wn.verb()) {
1943 case SkPath::kLine_Verb: {
1944 pts = LineIntersect(wt.fPts, wn.fPts, ts);
1945 debugShowLineIntersection(pts, wt, wn,
1946 ts.fT[0], ts.fT[1]);
1947 break;
1948 }
1949 case SkPath::kQuad_Verb: {
1950 swap = true;
1951 pts = QuadLineIntersect(wn.fPts, wt.fPts, ts);
1952 break;
1953 }
1954 case SkPath::kCubic_Verb: {
1955 swap = true;
1956 pts = CubicLineIntersect(wn.fPts, wt.fPts, ts);
1957 break;
1958 }
1959 default:
1960 SkASSERT(0);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001961 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001962 break;
1963 case SkPath::kQuad_Verb:
1964 switch (wn.verb()) {
1965 case SkPath::kLine_Verb: {
1966 pts = QuadLineIntersect(wt.fPts, wn.fPts, ts);
1967 break;
1968 }
1969 case SkPath::kQuad_Verb: {
1970 pts = QuadIntersect(wt.fPts, wn.fPts, ts);
1971 break;
1972 }
1973 case SkPath::kCubic_Verb: {
1974 // FIXME: promote quad to cubic
1975 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
1976 break;
1977 }
1978 default:
1979 SkASSERT(0);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00001980 }
caryclark@google.coma5764232012-03-28 16:20:21 +00001981 break;
1982 case SkPath::kCubic_Verb:
1983 switch (wn.verb()) {
1984 case SkPath::kLine_Verb: {
1985 pts = CubicLineIntersect(wt.fPts, wn.fPts, ts);
1986 break;
1987 }
1988 case SkPath::kQuad_Verb: {
1989 // FIXME: promote quad to cubic
1990 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
1991 break;
1992 }
1993 case SkPath::kCubic_Verb: {
1994 pts = CubicIntersect(wt.fPts, wn.fPts, ts);
1995 break;
1996 }
1997 default:
1998 SkASSERT(0);
1999 }
2000 break;
2001 default:
2002 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002003 }
caryclark@google.coma5764232012-03-28 16:20:21 +00002004 test->add(ts.fT[swap], pts, wt.verbIndex());
2005 next->add(ts.fT[!swap], pts, wn.verbIndex());
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002006 } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
2007 } while (nextPtr != lastPtr);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002008 }
2009}
2010
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002011static InEdge** advanceEdges(SkTDArray<ActiveEdge>* activeEdges,
caryclark@google.com6008c6562012-02-15 22:01:16 +00002012 InEdge** currentPtr, InEdge** lastPtr, SkScalar y) {
2013 InEdge** testPtr = currentPtr - 1;
2014 while (++testPtr != lastPtr) {
2015 if ((*testPtr)->fBounds.fBottom > y) {
2016 continue;
2017 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002018 if (activeEdges) {
2019 InEdge* test = *testPtr;
2020 ActiveEdge* activePtr = activeEdges->begin() - 1;
2021 ActiveEdge* lastActive = activeEdges->end();
2022 while (++activePtr != lastActive) {
2023 if (activePtr->fWorkEdge.fEdge == test) {
2024 activeEdges->remove(activePtr - activeEdges->begin());
2025 break;
2026 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002027 }
2028 }
2029 if (testPtr == currentPtr) {
2030 ++currentPtr;
2031 }
2032 }
2033 return currentPtr;
2034}
2035
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002036// OPTIMIZE: inline?
2037static HorizontalEdge** advanceHorizontal(HorizontalEdge** edge, SkScalar y) {
2038 while ((*edge)->fY < y) {
2039 ++edge;
2040 }
2041 return edge;
2042}
2043
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002044// compute bottom taking into account any intersected edges
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002045static SkScalar computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
2046 SkScalar y, SkScalar bottom) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002047 ActiveEdge* activePtr = activeEdges.begin() - 1;
2048 ActiveEdge* lastActive = activeEdges.end();
2049 while (++activePtr != lastActive) {
2050 const InEdge* test = activePtr->fWorkEdge.fEdge;
2051 if (!test->fContainsIntercepts) {
2052 continue;
2053 }
2054 WorkEdge wt;
2055 wt.init(test);
2056 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002057 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
caryclark@google.com4917f172012-03-05 22:01:21 +00002058 if (intercepts.fTopIntercepts > 1) {
2059 SkScalar yTop = wt.fPts[0].fY;
2060 if (yTop > y && bottom > yTop) {
2061 bottom = yTop;
2062 }
2063 }
2064 if (intercepts.fBottomIntercepts > 1) {
2065 SkScalar yBottom = wt.fPts[wt.verb()].fY;
2066 if (yBottom > y && bottom > yBottom) {
2067 bottom = yBottom;
2068 }
2069 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002070 const SkTDArray<double>& fTs = intercepts.fTs;
2071 size_t count = fTs.count();
2072 for (size_t index = 0; index < count; ++index) {
caryclark@google.coma5764232012-03-28 16:20:21 +00002073 SkScalar yIntercept;
2074 switch (wt.verb()) {
2075 case SkPath::kLine_Verb: {
2076 yIntercept = LineYAtT(wt.fPts, fTs[index]);
2077 break;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002078 }
caryclark@google.coma5764232012-03-28 16:20:21 +00002079 case SkPath::kQuad_Verb: {
2080 yIntercept = QuadYAtT(wt.fPts, fTs[index]);
2081 break;
2082 }
2083 case SkPath::kCubic_Verb: {
2084 yIntercept = CubicYAtT(wt.fPts, fTs[index]);
2085 break;
2086 }
2087 default:
2088 SkASSERT(0); // should never get here
2089 }
2090 if (yIntercept > y && bottom > yIntercept) {
2091 bottom = yIntercept;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002092 }
2093 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002094 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002095 }
caryclark@google.com198e0542012-03-30 18:47:02 +00002096#if DEBUG_BOTTOM
2097 SkDebugf("%s bottom=%1.9g\n", __FUNCTION__, bottom);
2098#endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002099 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002100}
2101
2102static SkScalar findBottom(InEdge** currentPtr,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002103 InEdge** edgeListEnd, SkTDArray<ActiveEdge>* activeEdges, SkScalar y,
caryclark@google.com6008c6562012-02-15 22:01:16 +00002104 bool asFill, InEdge**& testPtr) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002105 InEdge* current = *currentPtr;
2106 SkScalar bottom = current->fBounds.fBottom;
caryclark@google.com752b60e2012-03-22 21:11:17 +00002107
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002108 // find the list of edges that cross y
caryclark@google.com6008c6562012-02-15 22:01:16 +00002109 InEdge* test = *testPtr;
2110 while (testPtr != edgeListEnd) {
2111 SkScalar testTop = test->fBounds.fTop;
2112 if (bottom <= testTop) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002113 break;
2114 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002115 SkScalar testBottom = test->fBounds.fBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002116 // OPTIMIZATION: Shortening the bottom is only interesting when filling
2117 // and when the edge is to the left of a longer edge. If it's a framing
2118 // edge, or part of the right, it won't effect the longer edges.
caryclark@google.com6008c6562012-02-15 22:01:16 +00002119 if (testTop > y) {
2120 bottom = testTop;
2121 break;
2122 }
2123 if (y < testBottom) {
2124 if (bottom > testBottom) {
2125 bottom = testBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002126 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002127 if (activeEdges) {
2128 addToActive(*activeEdges, test);
2129 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002130 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00002131 test = *++testPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002132 }
caryclark@google.com198e0542012-03-30 18:47:02 +00002133#if DEBUG_BOTTOM
2134 SkDebugf("%s %d bottom=%1.9g\n", __FUNCTION__, activeEdges ? 2 : 1, bottom);
2135#endif
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002136 return bottom;
2137}
2138
2139static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
2140 SkTDArray<InEdge*>& edgeList) {
2141 size_t edgeCount = edges.count();
2142 if (edgeCount == 0) {
2143 return;
2144 }
caryclark@google.comfb173422012-04-10 18:28:55 +00002145 int id = 0;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002146 for (size_t index = 0; index < edgeCount; ++index) {
caryclark@google.comfb173422012-04-10 18:28:55 +00002147 InEdge& edge = edges[index];
2148 if (!edge.fWinding) {
2149 continue;
2150 }
2151 edge.fID = ++id;
2152 *edgeList.append() = &edge;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002153 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002154 *edgeList.append() = &edgeSentinel;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002155 QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002156}
2157
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002158static void makeHorizontalList(SkTDArray<HorizontalEdge>& edges,
2159 HorizontalEdge& edgeSentinel, SkTDArray<HorizontalEdge*>& edgeList) {
2160 size_t edgeCount = edges.count();
2161 if (edgeCount == 0) {
2162 return;
2163 }
2164 for (size_t index = 0; index < edgeCount; ++index) {
2165 *edgeList.append() = &edges[index];
2166 }
2167 edgeSentinel.fLeft = edgeSentinel.fRight = edgeSentinel.fY = SK_ScalarMax;
2168 *edgeList.append() = &edgeSentinel;
2169 QSort<HorizontalEdge>(edgeList.begin(), edgeList.end() - 1);
2170}
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002171
2172static void skipCoincidence(int lastWinding, int winding, int windingMask,
2173 ActiveEdge* activePtr, ActiveEdge* firstCoincident) {
2174 if (((lastWinding & windingMask) == 0) ^ (winding & windingMask) != 0) {
2175 return;
2176 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002177 // FIXME: ? shouldn't this be if (lastWinding & windingMask) ?
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002178 if (lastWinding) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002179#if DEBUG_ADJUST_COINCIDENT
2180 SkDebugf("%s edge=%d 1 set skip=false\n", __FUNCTION__, activePtr->ID());
2181#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002182 activePtr->fSkip = false;
2183 } else {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002184#if DEBUG_ADJUST_COINCIDENT
2185 SkDebugf("%s edge=%d 2 set skip=false\n", __FUNCTION__, firstCoincident->ID());
2186#endif
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002187 firstCoincident->fSkip = false;
2188 }
2189}
2190
caryclark@google.com6008c6562012-02-15 22:01:16 +00002191static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002192 SkTDArray<ActiveEdge*>& edgeList, SkScalar y) {
caryclark@google.com6008c6562012-02-15 22:01:16 +00002193 size_t edgeCount = activeEdges.count();
2194 if (edgeCount == 0) {
2195 return;
2196 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002197#if DEBUG_SORT_HORIZONTAL
caryclark@google.comd88e0892012-03-27 13:23:51 +00002198 const int tab = 3; // FIXME: debugging only
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002199 SkDebugf("%s y=%1.9g\n", __FUNCTION__, y);
2200#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00002201 size_t index;
2202 for (index = 0; index < edgeCount; ++index) {
2203 ActiveEdge& activeEdge = activeEdges[index];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002204 do {
2205 activeEdge.calcLeft(y);
2206 // skip segments that don't span y
2207 if (activeEdge.fAbove != activeEdge.fBelow) {
2208 break;
2209 }
2210 if (activeEdge.fDone) {
2211#if DEBUG_SORT_HORIZONTAL
2212 SkDebugf("%*s edge=%d done\n", tab, "", activeEdge.ID());
2213#endif
2214 goto nextEdge;
2215 }
2216#if DEBUG_SORT_HORIZONTAL
2217 SkDebugf("%*s edge=%d above==below\n", tab, "", activeEdge.ID());
2218#endif
2219 } while (activeEdge.advanceT() || activeEdge.advance());
2220#if DEBUG_SORT_HORIZONTAL
2221 SkDebugf("%*s edge=%d above=(%1.9g,%1.9g) (%1.9g) below=(%1.9g,%1.9g)"
2222 " (%1.9g)\n", tab, "", activeEdge.ID(),
2223 activeEdge.fAbove.fX, activeEdge.fAbove.fY, activeEdge.fTAbove,
2224 activeEdge.fBelow.fX, activeEdge.fBelow.fY, activeEdge.fTBelow);
2225#endif
2226 activeEdge.fSkip = activeEdge.fCloseCall = activeEdge.fFixBelow = false;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002227 *edgeList.append() = &activeEdge;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002228nextEdge:
2229 ;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002230 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002231 QSort<ActiveEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002232}
2233
2234// remove coincident edges
2235// OPTIMIZE: remove edges? This is tricky because the current logic expects
2236// the winding count to be maintained while skipping coincident edges. In
2237// addition to removing the coincident edges, the remaining edges would need
2238// to have a different winding value, possibly different per intercept span.
2239static SkScalar adjustCoincident(SkTDArray<ActiveEdge*>& edgeList,
2240 int windingMask, SkScalar y, SkScalar bottom, OutEdgeBuilder& outBuilder)
2241{
2242#if DEBUG_ADJUST_COINCIDENT
2243 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
2244#endif
2245 size_t edgeCount = edgeList.count();
2246 if (edgeCount == 0) {
2247 return bottom;
2248 }
caryclark@google.com78e17132012-04-17 11:40:34 +00002249 ActiveEdge* activePtr, * nextPtr = edgeList[0];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002250 size_t index;
2251 bool foundCoincident = false;
2252 int firstIndex = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002253 for (index = 1; index < edgeCount; ++index) {
caryclark@google.com78e17132012-04-17 11:40:34 +00002254 activePtr = nextPtr;
2255 nextPtr = edgeList[index];
2256 if (firstIndex != index - 1 && !activePtr->fSkip) {
2257 if (nextPtr->fWorkEdge.verb() == SkPath::kLine_Verb
2258 && activePtr->isUnordered(nextPtr)) {
2259 start here;
2260 // swap the line with the curve
2261 // back up to the previous edge and retest
2262 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
2263 SkASSERT(index > 1);
2264 index -= 2;
2265 continue;
2266 }
2267 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002268 bool closeCall = false;
2269 activePtr->fCoincident = firstIndex;
caryclark@google.com78e17132012-04-17 11:40:34 +00002270 if (activePtr->isCoincidentWith(nextPtr)
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002271 || (closeCall = activePtr->tooCloseToCall(nextPtr))) {
2272 activePtr->fSkip = nextPtr->fSkip = foundCoincident = true;
2273 activePtr->fCloseCall = nextPtr->fCloseCall = closeCall;
caryclark@google.com78e17132012-04-17 11:40:34 +00002274 } else if (activePtr->isUnordered(nextPtr)) {
2275 foundCoincident = true;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002276 } else {
2277 firstIndex = index;
2278 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002279 }
caryclark@google.com78e17132012-04-17 11:40:34 +00002280 nextPtr->fCoincident = firstIndex;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002281 if (!foundCoincident) {
2282 return bottom;
2283 }
2284 int winding = 0;
caryclark@google.com78e17132012-04-17 11:40:34 +00002285 nextPtr = edgeList[0];
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002286 for (index = 1; index < edgeCount; ++index) {
2287 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002288 winding += activePtr->fWorkEdge.winding();
caryclark@google.com78e17132012-04-17 11:40:34 +00002289 activePtr = nextPtr;
2290 nextPtr = edgeList[index];
2291 SkASSERT(activePtr == edgeList[index - 1]);
2292 SkASSERT(nextPtr == edgeList[index]);
2293 if (activePtr->fCoincident != nextPtr->fCoincident) {
2294 continue;
2295 }
2296 // the coincident edges may not have been sorted above -- advance
2297 // the edges and resort if needed
2298 // OPTIMIZE: if sorting is done incrementally as new edges are added
2299 // and not all at once as is done here, fold this test into the
2300 // current less than test.
2301 while ((!activePtr->fSkip || !nextPtr->fSkip)
2302 && activePtr->fCoincident == nextPtr->fCoincident) {
2303 if (activePtr->swapUnordered(nextPtr, bottom)) {
2304 winding -= activePtr->fWorkEdge.winding();
2305 SkASSERT(activePtr == edgeList[index - 1]);
2306 SkASSERT(nextPtr == edgeList[index]);
2307 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
2308 if (--index == 0) {
2309 winding += activePtr->fWorkEdge.winding();
2310 break;
2311 }
2312 // back up one
2313 activePtr = edgeList[index - 1];
2314 continue;
2315 }
2316 SkASSERT(activePtr == edgeList[index - 1]);
2317 SkASSERT(nextPtr == edgeList[index]);
2318 break;
2319 }
2320 if (activePtr->fSkip && nextPtr->fSkip) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002321 if (activePtr->fCloseCall ? activePtr->swapClose(nextPtr,
2322 priorWinding, winding, windingMask)
2323 : activePtr->swapCoincident(nextPtr, bottom)) {
caryclark@google.com4917f172012-03-05 22:01:21 +00002324 winding -= activePtr->fWorkEdge.winding();
caryclark@google.com78e17132012-04-17 11:40:34 +00002325 SkASSERT(activePtr == edgeList[index - 1]);
2326 SkASSERT(nextPtr == edgeList[index]);
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002327 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
2328 SkTSwap<ActiveEdge*>(activePtr, nextPtr);
caryclark@google.com4917f172012-03-05 22:01:21 +00002329 winding += activePtr->fWorkEdge.winding();
caryclark@google.com78e17132012-04-17 11:40:34 +00002330 SkASSERT(activePtr == edgeList[index - 1]);
2331 SkASSERT(nextPtr == edgeList[index]);
caryclark@google.comcd4421d2012-03-01 19:16:31 +00002332 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002333 }
2334 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002335 int firstCoincidentWinding = 0;
2336 ActiveEdge* firstCoincident = NULL;
2337 winding = 0;
2338 activePtr = edgeList[0];
2339 for (index = 1; index < edgeCount; ++index) {
2340 int priorWinding = winding;
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002341 winding += activePtr->fWorkEdge.winding();
caryclark@google.com78e17132012-04-17 11:40:34 +00002342 nextPtr = edgeList[index];
2343 if (activePtr->fSkip && nextPtr->fSkip
2344 && activePtr->fCoincident == nextPtr->fCoincident) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002345 if (!firstCoincident) {
2346 firstCoincident = activePtr;
2347 firstCoincidentWinding = priorWinding;
2348 }
2349 if (activePtr->fCloseCall) {
2350 // If one of the edges has already been added to out as a non
2351 // coincident edge, trim it back to the top of this span
2352 if (outBuilder.trimLine(y, activePtr->ID())) {
2353 activePtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002354 #if DEBUG_ADJUST_COINCIDENT
2355 SkDebugf("%s 1 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
2356 __FUNCTION__, activePtr->ID(), y, activePtr->fYBottom);
2357 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002358 activePtr->fYBottom = y;
2359 }
2360 if (outBuilder.trimLine(y, nextPtr->ID())) {
2361 nextPtr->addTatYAbove(y);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002362 #if DEBUG_ADJUST_COINCIDENT
2363 SkDebugf("%s 2 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
2364 __FUNCTION__, nextPtr->ID(), y, nextPtr->fYBottom);
2365 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002366 nextPtr->fYBottom = y;
2367 }
2368 // add missing t values so edges can be the same length
2369 SkScalar testY = activePtr->fBelow.fY;
2370 nextPtr->addTatYBelow(testY);
2371 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002372 #if DEBUG_ADJUST_COINCIDENT
2373 SkDebugf("%s 3 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
2374 __FUNCTION__, activePtr->ID(), testY, bottom);
2375 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002376 bottom = testY;
2377 }
2378 testY = nextPtr->fBelow.fY;
2379 activePtr->addTatYBelow(testY);
2380 if (bottom > testY && testY > y) {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002381 #if DEBUG_ADJUST_COINCIDENT
2382 SkDebugf("%s 4 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
2383 __FUNCTION__, nextPtr->ID(), testY, bottom);
2384 #endif
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002385 bottom = testY;
2386 }
2387 }
2388 } else if (firstCoincident) {
2389 skipCoincidence(firstCoincidentWinding, winding, windingMask,
2390 activePtr, firstCoincident);
2391 firstCoincident = NULL;
2392 }
2393 activePtr = nextPtr;
2394 }
2395 if (firstCoincident) {
2396 winding += activePtr->fWorkEdge.winding();
2397 skipCoincidence(firstCoincidentWinding, winding, windingMask, activePtr,
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00002398 firstCoincident);
2399 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002400 // fix up the bottom for close call edges. OPTIMIZATION: maybe this could
2401 // be in the loop above, but moved here since loop above reads fBelow and
2402 // it felt unsafe to write it in that loop
2403 for (index = 0; index < edgeCount; ++index) {
2404 (edgeList[index])->fixBelow();
2405 }
2406 return bottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002407}
2408
2409// stitch edge and t range that satisfies operation
caryclark@google.com6008c6562012-02-15 22:01:16 +00002410static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
caryclark@google.com752b60e2012-03-22 21:11:17 +00002411 SkScalar bottom, int windingMask, bool fill, OutEdgeBuilder& outBuilder) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002412 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00002413 ActiveEdge** activeHandle = edgeList.begin() - 1;
2414 ActiveEdge** lastActive = edgeList.end();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002415 const int tab = 7; // FIXME: debugging only
caryclark@google.com78e17132012-04-17 11:40:34 +00002416#if DEBUG_STITCH_EDGE
2417 SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
2418#endif
caryclark@google.com6008c6562012-02-15 22:01:16 +00002419 while (++activeHandle != lastActive) {
2420 ActiveEdge* activePtr = *activeHandle;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002421 const WorkEdge& wt = activePtr->fWorkEdge;
2422 int lastWinding = winding;
2423 winding += wt.winding();
caryclark@google.com78e17132012-04-17 11:40:34 +00002424#if DEBUG_STITCH_EDGE
2425 SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d"
2426 " above=%1.9g below=%1.9g\n",
2427 tab-4, "", activePtr->ID(), lastWinding,
2428 winding, activePtr->fSkip, activePtr->fCloseCall,
2429 activePtr->fTAbove, activePtr->fTBelow);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002430#endif
caryclark@google.com752b60e2012-03-22 21:11:17 +00002431 if (activePtr->done(bottom)) {
caryclark@google.com78e17132012-04-17 11:40:34 +00002432#if DEBUG_STITCH_EDGE
2433 SkDebugf("%*s fDone=%d || fYBottom=%1.9g >= bottom\n", tab, "",
2434 activePtr->fDone, activePtr->fYBottom);
2435#endif
caryclark@google.com752b60e2012-03-22 21:11:17 +00002436 continue;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002437 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00002438 int opener = (lastWinding & windingMask) == 0;
2439 bool closer = (winding & windingMask) == 0;
2440 SkASSERT(!opener | !closer);
2441 bool inWinding = opener | closer;
caryclark@google.coma5764232012-03-28 16:20:21 +00002442 SkPoint clippedPts[4];
caryclark@google.com4917f172012-03-05 22:01:21 +00002443 const SkPoint* clipped = NULL;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002444 bool moreToDo, aboveBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002445 do {
2446 double currentT = activePtr->t();
caryclark@google.com78e17132012-04-17 11:40:34 +00002447 uint8_t verb = wt.verb();
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002448 const SkPoint* points = wt.fPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002449 double nextT;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002450 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002451 nextT = activePtr->nextT();
caryclark@google.coma5764232012-03-28 16:20:21 +00002452 // FIXME: obtuse: want efficient way to say
2453 // !currentT && currentT != 1 || !nextT && nextT != 1
2454 if (currentT * nextT != 0 || currentT + nextT != 1) {
2455 // OPTIMIZATION: if !inWinding, we only need
2456 // clipped[1].fY
2457 switch (verb) {
2458 case SkPath::kLine_Verb:
2459 LineSubDivide(points, currentT, nextT, clippedPts);
2460 break;
2461 case SkPath::kQuad_Verb:
2462 QuadSubDivide(points, currentT, nextT, clippedPts);
2463 break;
2464 case SkPath::kCubic_Verb:
2465 CubicSubDivide(points, currentT, nextT, clippedPts);
2466 break;
2467 default:
2468 SkASSERT(0);
2469 break;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002470 }
caryclark@google.coma5764232012-03-28 16:20:21 +00002471 clipped = clippedPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002472 } else {
caryclark@google.coma5764232012-03-28 16:20:21 +00002473 clipped = points;
2474 }
2475 if (inWinding && !activePtr->fSkip && (fill ? clipped[0].fY
2476 != clipped[verb].fY : clipped[0] != clipped[verb])) {
caryclark@google.comfb173422012-04-10 18:28:55 +00002477#if DEBUG_STITCH_EDGE
2478 SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d"
2479 " v=%d t=(%1.9g,%1.9g)\n", tab, "",
2480 kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
2481 clipped[verb].fX, clipped[verb].fY,
2482 activePtr->ID(),
2483 activePtr->fWorkEdge.fVerb
2484 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
2485 currentT, nextT);
2486#endif
caryclark@google.coma5764232012-03-28 16:20:21 +00002487 outBuilder.addCurve(clipped, (SkPath::Verb) verb,
2488 activePtr->fWorkEdge.fEdge->fID,
2489 activePtr->fCloseCall);
2490 } else {
caryclark@google.comfb173422012-04-10 18:28:55 +00002491#if DEBUG_STITCH_EDGE
2492 SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g"
2493 " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
2494 kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
2495 clipped[verb].fX, clipped[verb].fY,
2496 activePtr->ID(),
2497 activePtr->fWorkEdge.fVerb
2498 - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
2499 currentT, nextT);
2500#endif
caryclark@google.coma5764232012-03-28 16:20:21 +00002501 }
2502 // by advancing fAbove/fBelow, the next call to sortHorizontal
2503 // will use these values if they're still valid instead of
2504 // recomputing
caryclark@google.com78e17132012-04-17 11:40:34 +00002505 if (clipped[verb].fY > activePtr->fBelow.fY
caryclark@google.coma5764232012-03-28 16:20:21 +00002506 && bottom >= activePtr->fBelow.fY ) {
2507 activePtr->fAbove = activePtr->fBelow;
caryclark@google.com78e17132012-04-17 11:40:34 +00002508 activePtr->fBelow = clipped[verb];
2509 activePtr->fTAbove = activePtr->fTBelow < 1
2510 ? activePtr->fTBelow : 0;
caryclark@google.coma5764232012-03-28 16:20:21 +00002511 activePtr->fTBelow = nextT;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002512 }
2513 currentT = nextT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002514 moreToDo = activePtr->advanceT();
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002515 activePtr->fYBottom = clipped[verb].fY; // was activePtr->fCloseCall ? bottom :
2516
2517 // clearing the fSkip/fCloseCall bit here means that trailing edges
2518 // fall out of sync, if one edge is long and another is a series of short pieces
2519 // if fSkip/fCloseCall is set, need to recompute coincidence/too-close-to-call
2520 // after advancing
2521 // another approach would be to restrict bottom to smaller part of close call
2522 // maybe this is already happening with coincidence when intersection is computed,
2523 // and needs to be added to the close call computation as well
2524 // this is hard to do because that the bottom is important is not known when
2525 // the lines are intersected; only when the computation for edge sorting is done
2526 // does the need for new bottoms become apparent.
2527 // maybe this is good incentive to scrap the current sort and do an insertion
2528 // sort that can take this into consideration when the x value is computed
2529
2530 // FIXME: initialized in sortHorizontal, cleared here as well so
2531 // that next edge is not skipped -- but should skipped edges ever
2532 // continue? (probably not)
caryclark@google.com752b60e2012-03-22 21:11:17 +00002533 aboveBottom = clipped[verb].fY < bottom;
2534 if (clipped[0].fY != clipped[verb].fY) {
2535 activePtr->fSkip = false;
2536 activePtr->fCloseCall = false;
2537 aboveBottom &= !activePtr->fCloseCall;
caryclark@google.com78e17132012-04-17 11:40:34 +00002538 }
2539#if DEBUG_STITCH_EDGE
2540 else {
caryclark@google.com752b60e2012-03-22 21:11:17 +00002541 if (activePtr->fSkip || activePtr->fCloseCall) {
caryclark@google.com78e17132012-04-17 11:40:34 +00002542 SkDebugf("%s skip or close == %1.9g\n", __FUNCTION__,
2543 clippedPts[0].fY);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002544 }
2545 }
caryclark@google.com78e17132012-04-17 11:40:34 +00002546#endif
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002547 } while (moreToDo & aboveBottom);
2548 } while ((moreToDo || activePtr->advance()) & aboveBottom);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002549 }
2550}
2551
caryclark@google.com198e0542012-03-30 18:47:02 +00002552static void dumpEdgeList(const SkTDArray<InEdge*>& edgeList,
2553 const InEdge& edgeSentinel) {
2554#if DEBUG_DUMP
2555 InEdge** debugPtr = edgeList.begin();
2556 do {
2557 (*debugPtr++)->dump();
2558 } while (*debugPtr != &edgeSentinel);
2559#endif
2560}
2561
caryclark@google.comc6825902012-02-03 22:07:47 +00002562void simplify(const SkPath& path, bool asFill, SkPath& simple) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002563 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
2564 int windingMask = (path.getFillType() & 1) ? 1 : -1;
2565 simple.reset();
2566 simple.setFillType(SkPath::kEvenOdd_FillType);
caryclark@google.comc6825902012-02-03 22:07:47 +00002567 // turn path into list of edges increasing in y
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00002568 // if an edge is a quad or a cubic with a y extrema, note it, but leave it
2569 // unbroken. Once we have a list, sort it, then walk the list (walk edges
2570 // twice that have y extrema's on top) and detect crossings -- look for raw
2571 // bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +00002572 SkTArray<InEdge> edges;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002573 SkTDArray<HorizontalEdge> horizontalEdges;
2574 InEdgeBuilder builder(path, asFill, edges, horizontalEdges);
caryclark@google.com6680fb12012-02-07 22:10:51 +00002575 SkTDArray<InEdge*> edgeList;
caryclark@google.com6680fb12012-02-07 22:10:51 +00002576 InEdge edgeSentinel;
caryclark@google.comfb173422012-04-10 18:28:55 +00002577 edgeSentinel.reset();
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002578 makeEdgeList(edges, edgeSentinel, edgeList);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002579 SkTDArray<HorizontalEdge*> horizontalList;
2580 HorizontalEdge horizontalSentinel;
2581 makeHorizontalList(horizontalEdges, horizontalSentinel, horizontalList);
caryclark@google.com6680fb12012-02-07 22:10:51 +00002582 InEdge** currentPtr = edgeList.begin();
caryclark@google.com4917f172012-03-05 22:01:21 +00002583 if (!currentPtr) {
2584 return;
2585 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002586 // find all intersections between edges
2587// beyond looking for horizontal intercepts, we need to know if any active edges
2588// intersect edges below 'bottom', but above the active edge segment.
2589// maybe it makes more sense to compute all intercepts before doing anything
2590// else, since the intercept list is long-lived, at least in the current design.
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002591 SkScalar y = (*currentPtr)->fBounds.fTop;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002592 HorizontalEdge** currentHorizontal = horizontalList.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00002593 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +00002594 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002595 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002596 NULL, y, asFill, lastPtr);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002597 if (lastPtr > currentPtr) {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002598 if (currentHorizontal) {
2599 if ((*currentHorizontal)->fY < SK_ScalarMax) {
2600 addBottomT(currentPtr, lastPtr, currentHorizontal);
2601 }
2602 currentHorizontal = advanceHorizontal(currentHorizontal, bottom);
2603 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00002604 addIntersectingTs(currentPtr, lastPtr);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002605 }
2606 y = bottom;
2607 currentPtr = advanceEdges(NULL, currentPtr, lastPtr, y);
2608 } while (*currentPtr != &edgeSentinel);
caryclark@google.com198e0542012-03-30 18:47:02 +00002609 // if a quadratic or cubic now has an intermediate T value, see if the Ts
2610 // on either side cause the Y values to monotonically increase. If not, split
2611 // the curve at the new T.
2612 if (builder.containsCurves()) {
2613 currentPtr = edgeList.begin();
2614 SkTArray<InEdge> splits;
2615 do {
caryclark@google.comfb173422012-04-10 18:28:55 +00002616 (*currentPtr)->splitInflectionPts(splits);
caryclark@google.com198e0542012-03-30 18:47:02 +00002617 } while (*++currentPtr != &edgeSentinel);
2618 if (splits.count()) {
caryclark@google.com198e0542012-03-30 18:47:02 +00002619 for (int index = 0; index < splits.count(); ++index) {
2620 edges.push_back(splits[index]);
2621 }
caryclark@google.comfb173422012-04-10 18:28:55 +00002622 edgeList.reset();
caryclark@google.com198e0542012-03-30 18:47:02 +00002623 makeEdgeList(edges, edgeSentinel, edgeList);
2624 }
2625 }
2626 dumpEdgeList(edgeList, edgeSentinel);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002627 // walk the sorted edges from top to bottom, computing accumulated winding
2628 SkTDArray<ActiveEdge> activeEdges;
2629 OutEdgeBuilder outBuilder(asFill);
2630 currentPtr = edgeList.begin();
2631 y = (*currentPtr)->fBounds.fTop;
2632 do {
2633 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
2634 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
2635 &activeEdges, y, asFill, lastPtr);
2636 if (lastPtr > currentPtr) {
2637 bottom = computeInterceptBottom(activeEdges, y, bottom);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002638 SkTDArray<ActiveEdge*> activeEdgeList;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002639 sortHorizontal(activeEdges, activeEdgeList, y);
2640 bottom = adjustCoincident(activeEdgeList, windingMask, y, bottom,
2641 outBuilder);
caryclark@google.com752b60e2012-03-22 21:11:17 +00002642 stitchEdge(activeEdgeList, y, bottom, windingMask, asFill, outBuilder);
caryclark@google.comc17972e2012-02-20 21:33:22 +00002643 }
caryclark@google.comc6825902012-02-03 22:07:47 +00002644 y = bottom;
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00002645 // OPTIMIZATION: as edges expire, InEdge allocations could be released
2646 currentPtr = advanceEdges(&activeEdges, currentPtr, lastPtr, y);
caryclark@google.comc6825902012-02-03 22:07:47 +00002647 } while (*currentPtr != &edgeSentinel);
caryclark@google.comc6825902012-02-03 22:07:47 +00002648 // assemble output path from string of pts, verbs
caryclark@google.comf8b000d2012-02-09 22:04:27 +00002649 outBuilder.bridge();
2650 outBuilder.assemble(simple);
caryclark@google.comc6825902012-02-03 22:07:47 +00002651}