blob: f07f8370e07c0cf7e5e63ff699efc0e4aad4f565 [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
20
caryclark@google.comfa0588f2012-04-26 21:01:06 +000021// FIXME: remove once debugging is complete
22#if 0 // set to 1 for no debugging whatsoever
23
24//const bool gxRunTestsInOneThread = false;
25
26#define DEBUG_ADD_INTERSECTING_TS 0
27#define DEBUG_BRIDGE 0
28#define DEBUG_DUMP 0
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000029#define DEBUG_PATH_CONSTRUCTION 0
30#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa0588f2012-04-26 21:01:06 +000031
32#else
33
34//const bool gRunTestsInOneThread = true;
35
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000036#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000037#define DEBUG_BRIDGE 1
38#define DEBUG_DUMP 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000039#define DEBUG_PATH_CONSTRUCTION 1
40#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa0588f2012-04-26 21:01:06 +000041
42#endif
43
44#if DEBUG_DUMP
45static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000046// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000047static int gContourID;
48static int gSegmentID;
49#endif
50
51static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
52 Intersections& intersections) {
53 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
54 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
55 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
56}
57
58static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
59 Intersections& intersections) {
60 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
61 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
62 intersect(aQuad, bLine, intersections);
63 return intersections.fUsed;
64}
65
66static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
67 Intersections& intersections) {
68 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
69 {a[3].fX, a[3].fY}};
70 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
71 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
72}
73
74static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
75 Intersections& intersections) {
76 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
77 const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
78 intersect(aQuad, bQuad, intersections);
79 return intersections.fUsed;
80}
81
82static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
83 Intersections& intersections) {
84 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
85 {a[3].fX, a[3].fY}};
86 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
87 {b[3].fX, b[3].fY}};
88 intersect(aCubic, bCubic, intersections);
89 return intersections.fUsed;
90}
91
92static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
93 SkScalar y, bool flipped, Intersections& intersections) {
94 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
95 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
96}
97
98static int VLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
99 SkScalar y, bool flipped, Intersections& intersections) {
100 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
101 return verticalIntersect(aLine, left, right, y, flipped, intersections);
102}
103
104static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
105 SkScalar y, bool flipped, Intersections& intersections) {
106 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
107 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
108}
109
110static int VQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
111 SkScalar y, bool flipped, Intersections& intersections) {
112 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
113 return verticalIntersect(aQuad, left, right, y, flipped, intersections);
114}
115
116static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
117 SkScalar y, bool flipped, Intersections& intersections) {
118 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
119 {a[3].fX, a[3].fY}};
120 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
121}
122
123static int VCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
124 SkScalar y, bool flipped, Intersections& intersections) {
125 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
126 {a[3].fX, a[3].fY}};
127 return verticalIntersect(aCubic, left, right, y, flipped, intersections);
128}
129
130static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
131 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
132 double x, y;
133 xy_at_t(line, t, x, y);
134 out->fX = SkDoubleToScalar(x);
135 out->fY = SkDoubleToScalar(y);
136}
137
138static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
139 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
140 double x, y;
141 xy_at_t(quad, t, x, y);
142 out->fX = SkDoubleToScalar(x);
143 out->fY = SkDoubleToScalar(y);
144}
145
146static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
147 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
148 {a[3].fX, a[3].fY}};
149 double x, y;
150 xy_at_t(cubic, t, x, y);
151 out->fX = SkDoubleToScalar(x);
152 out->fY = SkDoubleToScalar(y);
153}
154
155static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
156 NULL,
157 LineXYAtT,
158 QuadXYAtT,
159 CubicXYAtT
160};
161
162static SkScalar LineXAtT(const SkPoint a[2], double t) {
163 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
164 double x;
165 xy_at_t(aLine, t, x, *(double*) 0);
166 return SkDoubleToScalar(x);
167}
168
169static SkScalar QuadXAtT(const SkPoint a[3], double t) {
170 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
171 double x;
172 xy_at_t(quad, t, x, *(double*) 0);
173 return SkDoubleToScalar(x);
174}
175
176static SkScalar CubicXAtT(const SkPoint a[4], double t) {
177 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
178 {a[3].fX, a[3].fY}};
179 double x;
180 xy_at_t(cubic, t, x, *(double*) 0);
181 return SkDoubleToScalar(x);
182}
183
184static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
185 NULL,
186 LineXAtT,
187 QuadXAtT,
188 CubicXAtT
189};
190
191static SkScalar LineYAtT(const SkPoint a[2], double t) {
192 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
193 double y;
194 xy_at_t(aLine, t, *(double*) 0, y);
195 return SkDoubleToScalar(y);
196}
197
198static SkScalar QuadYAtT(const SkPoint a[3], double t) {
199 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
200 double y;
201 xy_at_t(quad, t, *(double*) 0, y);
202 return SkDoubleToScalar(y);
203}
204
205static SkScalar CubicYAtT(const SkPoint a[4], double t) {
206 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
207 {a[3].fX, a[3].fY}};
208 double y;
209 xy_at_t(cubic, t, *(double*) 0, y);
210 return SkDoubleToScalar(y);
211}
212
213static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
214 NULL,
215 LineYAtT,
216 QuadYAtT,
217 CubicYAtT
218};
219
220static void LineSubDivide(const SkPoint a[2], double startT, double endT,
221 SkPoint sub[2]) {
222 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
223 _Line dst;
224 sub_divide(aLine, startT, endT, dst);
225 sub[0].fX = SkDoubleToScalar(dst[0].x);
226 sub[0].fY = SkDoubleToScalar(dst[0].y);
227 sub[1].fX = SkDoubleToScalar(dst[1].x);
228 sub[1].fY = SkDoubleToScalar(dst[1].y);
229}
230
231static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
232 SkPoint sub[3]) {
233 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
234 {a[2].fX, a[2].fY}};
235 Quadratic dst;
236 sub_divide(aQuad, startT, endT, dst);
237 sub[0].fX = SkDoubleToScalar(dst[0].x);
238 sub[0].fY = SkDoubleToScalar(dst[0].y);
239 sub[1].fX = SkDoubleToScalar(dst[1].x);
240 sub[1].fY = SkDoubleToScalar(dst[1].y);
241 sub[2].fX = SkDoubleToScalar(dst[2].x);
242 sub[2].fY = SkDoubleToScalar(dst[2].y);
243}
244
245static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
246 SkPoint sub[4]) {
247 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
248 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
249 Cubic dst;
250 sub_divide(aCubic, startT, endT, dst);
251 sub[0].fX = SkDoubleToScalar(dst[0].x);
252 sub[0].fY = SkDoubleToScalar(dst[0].y);
253 sub[1].fX = SkDoubleToScalar(dst[1].x);
254 sub[1].fY = SkDoubleToScalar(dst[1].y);
255 sub[2].fX = SkDoubleToScalar(dst[2].x);
256 sub[2].fY = SkDoubleToScalar(dst[2].y);
257 sub[3].fX = SkDoubleToScalar(dst[3].x);
258 sub[3].fY = SkDoubleToScalar(dst[3].y);
259}
260
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000261static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
262 SkPoint []) = {
263 NULL,
264 LineSubDivide,
265 QuadSubDivide,
266 CubicSubDivide
267};
268
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000269#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000270static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
271 SkRect& bounds) {
272 SkPoint dst[3];
273 QuadSubDivide(a, startT, endT, dst);
274 bounds.fLeft = bounds.fRight = dst[0].fX;
275 bounds.fTop = bounds.fBottom = dst[0].fY;
276 for (int index = 1; index < 3; ++index) {
277 bounds.growToInclude(dst[index].fX, dst[index].fY);
278 }
279}
280
281static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
282 SkRect& bounds) {
283 SkPoint dst[4];
284 CubicSubDivide(a, startT, endT, dst);
285 bounds.fLeft = bounds.fRight = dst[0].fX;
286 bounds.fTop = bounds.fBottom = dst[0].fY;
287 for (int index = 1; index < 4; ++index) {
288 bounds.growToInclude(dst[index].fX, dst[index].fY);
289 }
290}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000291#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000292
caryclark@google.com15fa1382012-05-07 20:49:36 +0000293static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000294 SkTDArray<SkPoint>& reducePts) {
295 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
296 {a[2].fX, a[2].fY}};
297 Quadratic dst;
298 int order = reduceOrder(aQuad, dst);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000299 if (order == 3) {
300 return SkPath::kQuad_Verb;
301 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000302 for (int index = 0; index < order; ++index) {
303 SkPoint* pt = reducePts.append();
304 pt->fX = SkDoubleToScalar(dst[index].x);
305 pt->fY = SkDoubleToScalar(dst[index].y);
306 }
307 return (SkPath::Verb) (order - 1);
308}
309
310static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
311 SkTDArray<SkPoint>& reducePts) {
312 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
313 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
314 Cubic dst;
315 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000316 if (order == 4) {
317 return SkPath::kCubic_Verb;
318 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000319 for (int index = 0; index < order; ++index) {
320 SkPoint* pt = reducePts.append();
321 pt->fX = SkDoubleToScalar(dst[index].x);
322 pt->fY = SkDoubleToScalar(dst[index].y);
323 }
324 return (SkPath::Verb) (order - 1);
325}
326
caryclark@google.com15fa1382012-05-07 20:49:36 +0000327static bool QuadIsLinear(const SkPoint a[3]) {
328 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
329 {a[2].fX, a[2].fY}};
330 return isLinear(aQuad, 0, 2);
331}
332
333static bool CubicIsLinear(const SkPoint a[4]) {
334 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
335 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
336 return isLinear(aCubic, 0, 3);
337}
338
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000339static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
340 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
341 double x[2];
342 xy_at_t(aLine, startT, x[0], *(double*) 0);
343 xy_at_t(aLine, endT, x[0], *(double*) 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000344 return startT < endT ? (float) startT : (float) endT;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000345}
346
347static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
348 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
349 {a[2].fX, a[2].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000350 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000351}
352
353static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
354 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
355 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000356 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000357}
358
359static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
360 NULL,
361 LineLeftMost,
362 QuadLeftMost,
363 CubicLeftMost
364};
365
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000366#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000367static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
368 const SkPoint& below) {
369 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
370 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
371 return implicit_matches_ulps(aLine, bLine, 32);
372}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000373#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000374
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000375class Segment;
376
caryclark@google.com15fa1382012-05-07 20:49:36 +0000377// sorting angles
378// given angles of {dx dy ddx ddy dddx dddy} sort them
379class Angle {
380public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000381 // FIXME: this is bogus for quads and cubics
382 // if the quads and cubics' line from end pt to ctrl pt are coincident,
383 // there's no obvious way to determine the curve ordering from the
384 // derivatives alone. In particular, if one quadratic's coincident tangent
385 // is longer than the other curve, the final control point can place the
386 // longer curve on either side of the shorter one.
387 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
388 // may provide some help, but nothing has been figured out yet.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000389 bool operator<(const Angle& rh) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000390 if ((fDy < 0) ^ (rh.fDy < 0)) {
391 return fDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000392 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000393 if (fDy == 0 && rh.fDy == 0 && fDx != rh.fDx) {
394 return fDx < rh.fDx;
395 }
396 SkScalar cmp = fDx * rh.fDy - rh.fDx * fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000397 if (cmp) {
398 return cmp < 0;
399 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000400 if ((fDDy < 0) ^ (rh.fDDy < 0)) {
401 return fDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000402 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000403 if (fDDy == 0 && rh.fDDy == 0 && fDDx != rh.fDDx) {
404 return fDDx < rh.fDDx;
405 }
406 cmp = fDDx * rh.fDDy - rh.fDDx * fDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000407 if (cmp) {
408 return cmp < 0;
409 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000410 if ((fDDDy < 0) ^ (rh.fDDDy < 0)) {
411 return fDDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000412 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000413 if (fDDDy == 0 && rh.fDDDy == 0) {
414 return fDDDx < rh.fDDDx;
415 }
416 return fDDDx * rh.fDDDy < rh.fDDDx * fDDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000417 }
418
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000419 int end() const {
420 return fEnd;
421 }
422
423 // since all angles share a point, this needs to know which point
424 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
425 // practically, this should only be called by addAngle
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000426 void set(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000427 int start, int end, bool coincident) {
428 SkASSERT(start != end);
429 fSegment = segment;
430 fStart = start;
431 fEnd = end;
432 fCoincident = coincident;
433 fDx = pts[1].fX - pts[0].fX; // b - a
434 fDy = pts[1].fY - pts[0].fY;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000435 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000436 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000437 return;
438 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000439 fDDx = pts[2].fX - pts[1].fX - fDx; // a - 2b + c
440 fDDy = pts[2].fY - pts[1].fY - fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000441 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000442 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000443 return;
444 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000445 fDDDx = pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX;
446 fDDDy = pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY;
447 }
448
449 // noncoincident quads/cubics may have the same initial angle
450 // as lines, so must sort by derivatives as well
451 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000452 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000453 int start, int end, bool coincident) {
454 fSegment = segment;
455 fStart = start;
456 fEnd = end;
457 fCoincident = coincident;
458 fDx = pts[1].fX - pts[0].fX; // b - a
459 fDy = pts[1].fY - pts[0].fY;
460 if (verb == SkPath::kLine_Verb) {
461 fDDx = fDDy = fDDDx = fDDDy = 0;
462 return;
463 }
464 if (verb == SkPath::kQuad_Verb) {
465 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
466 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
467 int larger = std::max(abs(uplsX), abs(uplsY));
468 int shift = 0;
469 double flatT;
470 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
471 LineParameters implicitLine;
472 _Line tangent = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}};
473 implicitLine.lineEndPoints(tangent);
474 implicitLine.normalize();
475 while (larger > UlpsEpsilon * 1024) {
476 larger >>= 2;
477 ++shift;
478 flatT = 0.5 / (1 << shift);
479 QuadXYAtT(pts, flatT, &ddPt);
480 _Point _pt = {ddPt.fX, ddPt.fY};
481 double distance = implicitLine.pointDistance(_pt);
482 if (approximately_zero(distance)) {
483 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
484 break;
485 }
486 }
487 flatT = 0.5 / (1 << shift);
488 QuadXYAtT(pts, flatT, &ddPt);
489 fDDx = ddPt.fX - pts[0].fX;
490 fDDy = ddPt.fY - pts[0].fY;
491 SkASSERT(fDDx != 0 || fDDy != 0);
492 fDDDx = fDDDy = 0;
493 return;
494 }
495 SkASSERT(0); // FIXME: add cubic case
496 }
497
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000498 Segment* segment() const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000499 return fSegment;
500 }
501
502 int sign() const {
503 int result = fStart - fEnd >> 31 | 1;
504 SkASSERT(result == fStart < fEnd ? -1 : 1);
505 return result;
506 }
507
508 int start() const {
509 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000510 }
511
512private:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000513 SkScalar fDx;
514 SkScalar fDy;
515 SkScalar fDDx;
516 SkScalar fDDy;
517 SkScalar fDDDx;
518 SkScalar fDDDy;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000519 Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000520 int fStart;
521 int fEnd;
522 bool fCoincident;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000523};
524
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000525static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
526 int angleCount = angles.count();
527 int angleIndex;
528 angleList.setReserve(angleCount);
529 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
530 *angleList.append() = &angles[angleIndex];
531 }
532 QSort<Angle>(angleList.begin(), angleList.end() - 1);
533}
534
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000535// Bounds, unlike Rect, does not consider a vertical line to be empty.
536struct Bounds : public SkRect {
537 static bool Intersects(const Bounds& a, const Bounds& b) {
538 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
539 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
540 }
541
542 bool isEmpty() {
543 return fLeft > fRight || fTop > fBottom
544 || fLeft == fRight && fTop == fBottom
545 || isnan(fLeft) || isnan(fRight)
546 || isnan(fTop) || isnan(fBottom);
547 }
548
549 void setCubicBounds(const SkPoint a[4]) {
550 _Rect dRect;
551 Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
552 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
553 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000554 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
555 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000556 }
557
558 void setQuadBounds(const SkPoint a[3]) {
559 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
560 {a[2].fX, a[2].fY}};
561 _Rect dRect;
562 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000563 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
564 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000565 }
566};
567
caryclark@google.com15fa1382012-05-07 20:49:36 +0000568struct Span {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000569 double fT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000570 Segment* fOther;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000571 double fOtherT; // value at fOther[fOtherIndex].fT
572 int fOtherIndex; // can't be used during intersection
caryclark@google.com15fa1382012-05-07 20:49:36 +0000573 int fWinding; // accumulated from contours surrounding this one
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000574 // OPTIMIZATION: coincident needs only 2 bits (values are -1, 0, 1)
caryclark@google.com15fa1382012-05-07 20:49:36 +0000575 int fCoincident; // -1 start of coincidence, 0 no coincidence, 1 end
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000576 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000577};
578
579class Segment {
580public:
581 Segment() {
582#if DEBUG_DUMP
583 fID = ++gSegmentID;
584#endif
585 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000586
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000587 void addAngle(SkTDArray<Angle>& angles, int start, int end,
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000588 bool coincident) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000589 SkASSERT(start != end);
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000590 int smaller = start < end ? start : end;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000591 if (fTs[smaller].fDone) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000592 return;
593 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000594 SkPoint edge[4];
595 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
596 Angle* angle = angles.append();
597 angle->set(edge, fVerb, this, start, end, coincident);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000598 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000599
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000600 void addCubic(const SkPoint pts[4]) {
601 init(pts, SkPath::kCubic_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000602 fBounds.setCubicBounds(pts);
603 }
604
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000605 void addCurveTo(int start, int end, SkPath& path) {
606 SkPoint edge[4];
607 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000608 #if DEBUG_PATH_CONSTRUCTION
609 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
610 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
611 if (fVerb > 1) {
612 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
613 }
614 if (fVerb > 2) {
615 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
616 }
617 SkDebugf("\n");
618 #endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000619 switch (fVerb) {
620 case SkPath::kLine_Verb:
621 path.lineTo(edge[1].fX, edge[1].fY);
622 break;
623 case SkPath::kQuad_Verb:
624 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
625 break;
626 case SkPath::kCubic_Verb:
627 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
628 edge[3].fX, edge[3].fY);
629 break;
630 }
631 }
632
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000633 void addLine(const SkPoint pts[2]) {
634 init(pts, SkPath::kLine_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000635 fBounds.set(pts, 2);
636 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000637
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000638 void addMoveTo(int tIndex, SkPath& path) {
639 SkPoint pt;
640 double firstT = t(tIndex);
641 xyAtT(firstT, &pt);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000642 #if DEBUG_PATH_CONSTRUCTION
643 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
644 #endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000645 path.moveTo(pt.fX, pt.fY);
646 }
647
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000648 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000649 void addOtherT(int index, double otherT, int otherIndex) {
650 Span& span = fTs[index];
651 span.fOtherT = otherT;
652 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000653 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000654
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000655 void addQuad(const SkPoint pts[3]) {
656 init(pts, SkPath::kQuad_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000657 fBounds.setQuadBounds(pts);
658 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000659
caryclark@google.com15fa1382012-05-07 20:49:36 +0000660 int addT(double newT, Segment& other, int coincident) {
661 // FIXME: in the pathological case where there is a ton of intercepts,
662 // binary search?
663 int insertedAt = -1;
664 Span* span;
665 size_t tCount = fTs.count();
caryclark@google.com15fa1382012-05-07 20:49:36 +0000666 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
667 // OPTIMIZATION: if there are three or more identical Ts, then
668 // the fourth and following could be further insertion-sorted so
669 // that all the edges are clockwise or counterclockwise.
670 // This could later limit segment tests to the two adjacent
671 // neighbors, although it doesn't help with determining which
672 // circular direction to go in.
673 if (newT <= fTs[idx2].fT) {
674 insertedAt = idx2;
675 span = fTs.insert(idx2);
676 goto finish;
677 }
678 }
679 insertedAt = tCount;
680 span = fTs.append();
681finish:
682 span->fT = newT;
683 span->fOther = &other;
684 span->fWinding = 1;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000685 if (span->fDone = newT == 1) {
686 ++fDoneSpans;
687 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000688 span->fCoincident = coincident;
689 fCoincident |= coincident;
690 return insertedAt;
691 }
692
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000693 void addTwoAngles(int start, int end, const SkPoint& endLoc,
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000694 const Span* endSpan, bool startCo, SkTDArray<Angle>& angles) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000695 // add edge leading into junction
696 addAngle(angles, end, start, startCo);
697 // add edge leading away from junction
698 bool coincident;
699 int step = start < end ? 1 : -1;
700 int tIndex = nextSpan(end, step, endLoc, endSpan, NULL, coincident);
701 if (tIndex >= 0) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000702 lastSpan(tIndex, step, endLoc, endSpan->fT, coincident);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000703 addAngle(angles, end, tIndex, coincident);
704 }
705 }
706
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000707 const Bounds& bounds() const {
708 return fBounds;
709 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000710
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000711 void buildAngles(int index, int last, int step, const SkPoint& loc,
712 SkTDArray<Angle>& angles) const {
713 SkASSERT(index - last != 0);
714 SkASSERT((index - last < 0) ^ (step < 0));
715 int end = last + step;
716 do {
717 Span* span = &fTs[index];
718 Segment* other = span->fOther;
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000719 if (other->done()) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000720 continue;
721 }
722 // if there is only one live crossing, and no coincidence, continue
723 // in the same direction
724 // if there is coincidence, the only choice may be to reverse direction
725 // find edge on either side of intersection
726 int oIndex = span->fOtherIndex;
727 Span* otherSpan = &other->fTs[oIndex];
728 SkASSERT(otherSpan->fOther == this);
729 // if done == -1, prior span has already been processed
730 bool otherCo;
731 int localStep = step;
732 int next = other->nextSpan(oIndex, localStep, loc, otherSpan,
733 NULL, otherCo);
734 if (next < 0) {
735 localStep = -step;
736 next = other->nextSpan(oIndex, localStep, loc, otherSpan,
737 NULL, otherCo);
738 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000739 other->lastSpan(next, localStep, loc, otherSpan->fT, otherCo);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000740 // add candidate into and away from junction
741 other->addTwoAngles(next, oIndex, loc, span, otherCo, angles);
742
743 } while ((index += step) != end);
744 }
745
746 // figure out if the segment's ascending T goes clockwise or not
747 // not enough context to write this as shown
748 // instead, add all segments meeting at the top
749 // sort them using buildAngleList
750 // find the first in the sort
751 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000752 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000753 SkASSERT(0); // incomplete
754 return false;
755 }
756
caryclark@google.com15fa1382012-05-07 20:49:36 +0000757 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000758 SkASSERT(fDoneSpans <= fTs.count());
759 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000760 }
761
caryclark@google.com15fa1382012-05-07 20:49:36 +0000762 int findCoincidentEnd(int start) const {
763 int tCount = fTs.count();
764 SkASSERT(start < tCount);
765 const Span& span = fTs[start];
766 SkASSERT(span.fCoincident);
767 for (int index = start + 1; index < tCount; ++index) {
768 const Span& match = fTs[index];
769 if (match.fOther == span.fOther) {
770 SkASSERT(match.fCoincident);
771 return index;
772 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000773 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000774 SkASSERT(0); // should never get here
775 return -1;
776 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000777
caryclark@google.com15fa1382012-05-07 20:49:36 +0000778 // start is the index of the beginning T of this edge
779 // it is guaranteed to have an end which describes a non-zero length (?)
780 // winding -1 means ccw, 1 means cw
781 // step is in/out -1 or 1
782 // spanIndex is returned
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000783 Segment* findNext(int winding, int& startIndex, int& endIndex) {
784 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000785 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000786 SkASSERT(startIndex < endIndex ? startIndex < count - 1
787 : startIndex > 0);
788 Span* startSpan = &fTs[startIndex];
caryclark@google.com15fa1382012-05-07 20:49:36 +0000789 // FIXME:
790 // since Ts can be stepped either way, done markers must be careful
791 // not to assume that segment was only ascending in T. This shouldn't
792 // be a problem unless pathologically a segment can be partially
793 // ascending and partially descending -- maybe quads/cubic can do this?
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000794
795
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000796 int step = startIndex < endIndex ? 1 : -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000797 SkPoint startLoc; // OPTIMIZATION: store this in the t span?
798 xyAtT(startSpan->fT, &startLoc);
799 SkPoint endLoc;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000800 bool startCo;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000801 int end = nextSpan(startIndex, step, startLoc, startSpan, &endLoc,
802 startCo);
803 SkASSERT(end >= 0);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000804
805 // preflight for coincidence -- if present, it may change winding
806 // considerations and whether reversed edges can be followed
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000807 bool many;
808 int last = lastSpan(end, step, endLoc, fTs[end].fT, startCo, &many);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000809
810 // Discard opposing direction candidates if no coincidence was found.
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000811 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000812 Segment* other;
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000813 if (!many) {
814 // mark the smaller of startIndex, endIndex done, and all adjacent
815 // spans with the same T value (but not 'other' spans)
816 markDone(startIndex < endIndex ? startIndex : endIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000817 SkASSERT(!startCo);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000818 // move in winding direction until edge in correct direction
819 // balance wrong direction edges before finding correct one
820 // this requres that the intersection is angularly sorted
821 // for a single intersection, special case -- choose the opposite
822 // edge that steps the same
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000823 other = endSpan->fOther;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000824 startIndex = endSpan->fOtherIndex;
825 endIndex = startIndex + step;
826 SkASSERT(step < 0 ? endIndex >= 0 : endIndex < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +0000827 return other;
828 }
829
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000830 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +0000831 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000832 SkASSERT(startIndex - endIndex != 0);
833 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000834 addTwoAngles(startIndex, end, endLoc, endSpan, startCo, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000835 buildAngles(end, last, step, endLoc, angles);
836 SkTDArray<Angle*> sorted;
837 sortAngles(angles, sorted);
838 // find the starting edge
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000839 int firstIndex = -1;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000840 int angleCount = angles.count();
841 int angleIndex;
842 const Angle* angle;
843 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
844 angle = sorted[angleIndex];
845 if (angle->segment() == this && angle->start() == end &&
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000846 angle->end() == startIndex) {
847 firstIndex = angleIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000848 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000849 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000850 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000851 SkASSERT(firstIndex >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000852 winding += angle->sign();
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000853 int nextIndex = firstIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000854 const Angle* nextAngle;
855 do {
856 if (++nextIndex == angleCount) {
857 nextIndex = 0;
858 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000859 SkASSERT(nextIndex != firstIndex); // should never wrap around
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000860 nextAngle = sorted[nextIndex];
861 // OPTIMIZATION: Figure out all connections, given the initial
862 // winding info (e.g., accumulate winding in span for reuse)
863 winding -= nextAngle->sign();
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000864
865 // start here;
866 // if the winding is non-zero, nextAngle does not connect to
867 // current chain. We may be able to deduce whether it will be
868 // in some future chain or ignored altogether based on winding,
869 // but for the first cut, just detach it from this chain.
870 if (!winding) {
871 break;
872 }
873 // but how to detach? Maybe it is correct to mark both ends
874 // for all of the sorted angles as done, regardless of whether we
875 // also compute the connectedness and/or winding for the inner ones.
876
877 } while (true);
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000878 markDone(startIndex < endIndex ? startIndex : endIndex);
879 other = nextAngle->segment();
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000880 startIndex = nextAngle->start();
881 endIndex = nextAngle->end();
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000882 return other;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000883 }
884
885
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000886 // so the span needs to contain the pairing info found here
887 // this should include the winding computed for the edge, and
888 // what edge it connects to, and whether it is discarded
889 // (maybe discarded == abs(winding) > 1) ?
890 // only need derivatives for duration of sorting, add a new struct
891 // for pairings, remove extra spans that have zero length and
892 // reference an unused other
893 // for coincident, the last span on the other may be marked done
894 // (always?)
895
caryclark@google.com15fa1382012-05-07 20:49:36 +0000896 // if loop is exhausted, contour may be closed.
897 // FIXME: pass in close point so we can check for closure
898
899 // given a segment, and a sense of where 'inside' is, return the next
900 // segment. If this segment has an intersection, or ends in multiple
901 // segments, find the mate that continues the outside.
902 // note that if there are multiples, but no coincidence, we can limit
903 // choices to connections in the correct direction
904
905 // mark found segments as done
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000906
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000907 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000908 void findTooCloseToCall(int /* winding */ ) { // FIXME: winding should be considered
caryclark@google.com15fa1382012-05-07 20:49:36 +0000909 int count = fTs.count();
910 if (count < 3) { // require t=0, x, 1 at minimum
911 return;
912 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000913 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000914 int moCount;
915 Span* match;
916 Segment* mOther;
917 do {
918 match = &fTs[matchIndex];
919 mOther = match->fOther;
920 moCount = mOther->fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000921 if (moCount >= 3) {
922 break;
923 }
924 if (++matchIndex >= count) {
925 return;
926 }
927 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +0000928 SkPoint matchPt;
929 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
930 xyAtT(match->fT, &matchPt);
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000931 // look for a pair of nearby T values that map to the same (x,y) value
932 // if found, see if the pair of other segments share a common point. If
933 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000934 for (int index = matchIndex + 1; index < count; ++index) {
935 Span* test = &fTs[index];
936 Segment* tOther = test->fOther;
937 int toCount = tOther->fTs.count();
938 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000939 continue;
940 }
941 SkPoint testPt;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000942 xyAtT(test->fT, &testPt);
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000943 if (matchPt != testPt) {
944 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000945 moCount = toCount;
946 match = test;
947 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000948 matchPt = testPt;
949 continue;
950 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000951 int moStart = -1;
952 int moEnd = -1;
953 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000954 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000955 Span& moSpan = mOther->fTs[moIndex];
956 if (moSpan.fOther == this) {
957 if (moSpan.fOtherT == match->fT) {
958 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000959 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000960 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000961 continue;
962 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000963 if (moSpan.fOther == tOther) {
964 SkASSERT(moEnd == -1);
965 moEnd = moIndex;
966 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000967 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000968 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000969 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000970 continue;
971 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000972 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
973 if (moStartT == moEndT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000974 continue;
975 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000976 int toStart = -1;
977 int toEnd = -1;
978 double toStartT, toEndT;
979 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
980 Span& toSpan = tOther->fTs[toIndex];
981 if (toSpan.fOther == this) {
982 if (toSpan.fOtherT == test->fT) {
983 toStart = toIndex;
984 toStartT = toSpan.fT;
985 }
986 continue;
987 }
988 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
989 SkASSERT(toEnd == -1);
990 toEnd = toIndex;
991 toEndT = toSpan.fT;
992 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000993 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000994 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
995 if (toStart <= 0 || toEnd <= 0) {
996 continue;
997 }
998 if (toStartT == toEndT) {
999 continue;
1000 }
1001 // test to see if the segment between there and here is linear
1002 if (!mOther->isLinear(moStart, moEnd)
1003 || !tOther->isLinear(toStart, toEnd)) {
1004 continue;
1005 }
1006 mOther->fTs[moStart].fCoincident = -1;
1007 tOther->fTs[toStart].fCoincident = -1;
1008 mOther->fTs[moEnd].fCoincident = 1;
1009 tOther->fTs[toEnd].fCoincident = 1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001010 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001011 }
1012
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001013 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
1014 // and use more concise logic like the old edge walker code?
1015 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001016 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001017 // iterate through T intersections and return topmost
1018 // topmost tangent from y-min to first pt is closer to horizontal
1019 int firstT = 0;
1020 int lastT = 0;
1021 SkScalar topY = fPts[0].fY;
1022 int count = fTs.count();
1023 int index;
1024 for (index = 1; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001025 const Span& span = fTs[index];
1026 double t = span.fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001027 SkScalar yIntercept = t == 1 ? fPts[fVerb].fY : yAtT(t);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001028 if (topY > yIntercept) {
1029 topY = yIntercept;
1030 firstT = lastT = index;
1031 } else if (topY == yIntercept) {
1032 lastT = index;
1033 }
1034 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001035 // if there's only a pair of segments, go with the endpoint chosen above
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001036 if (firstT == lastT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001037 tIndex = firstT;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001038 endIndex = firstT > 0 ? tIndex - 1 : tIndex + 1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001039 return this;
1040 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001041 // sort the edges to find the leftmost
1042 SkPoint startLoc; // OPTIMIZATION: store this in the t span?
1043 const Span* startSpan = &fTs[firstT];
1044 xyAtT(startSpan->fT, &startLoc);
1045 SkPoint endLoc;
1046 bool nextCo;
1047 int end = nextSpan(firstT, 1, startLoc, startSpan, &endLoc, nextCo);
1048 if (end == -1) {
1049 end = nextSpan(firstT, -1, startLoc, startSpan, &endLoc, nextCo);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001050 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001051 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001052 // if the topmost T is not on end, or is three-way or more, find left
1053 // look for left-ness from tLeft to firstT (matching y of other)
1054 SkTDArray<Angle> angles;
1055 SkASSERT(firstT - end != 0);
1056 addTwoAngles(end, firstT, endLoc, &fTs[firstT], nextCo, angles);
1057 buildAngles(firstT, lastT, 1, startLoc, angles);
1058 SkTDArray<Angle*> sorted;
1059 sortAngles(angles, sorted);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001060 Segment* leftSegment = sorted[0]->segment();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001061 tIndex = sorted[0]->end();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001062 endIndex = sorted[0]->start();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001063 return leftSegment;
1064 }
1065
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001066 // FIXME: not crazy about this
1067 // when the intersections are performed, the other index is into an
1068 // incomplete array. as the array grows, the indices become incorrect
1069 // while the following fixes the indices up again, it isn't smart about
1070 // skipping segments whose indices are already correct
1071 // assuming we leave the code that wrote the index in the first place
1072 void fixOtherTIndex() {
1073 int iCount = fTs.count();
1074 for (int i = 0; i < iCount; ++i) {
1075 Span& iSpan = fTs[i];
1076 double oT = iSpan.fOtherT;
1077 Segment* other = iSpan.fOther;
1078 int oCount = other->fTs.count();
1079 for (int o = 0; o < oCount; ++o) {
1080 Span& oSpan = other->fTs[o];
1081 if (oT == oSpan.fT && this == oSpan.fOther) {
1082 iSpan.fOtherIndex = o;
1083 }
1084 }
1085 }
1086 }
1087
1088 void init(const SkPoint pts[], SkPath::Verb verb) {
1089 fPts = pts;
1090 fVerb = verb;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001091 fDoneSpans = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001092 fCoincident = 0;
1093 }
1094
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001095 bool intersected() const {
1096 return fTs.count() > 0;
1097 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001098
1099 bool isLinear(int start, int end) const {
1100 if (fVerb == SkPath::kLine_Verb) {
1101 return true;
1102 }
1103 if (fVerb == SkPath::kQuad_Verb) {
1104 SkPoint qPart[3];
1105 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
1106 return QuadIsLinear(qPart);
1107 } else {
1108 SkASSERT(fVerb == SkPath::kCubic_Verb);
1109 SkPoint cPart[4];
1110 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
1111 return CubicIsLinear(cPart);
1112 }
1113 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001114
1115 bool isHorizontal() const {
1116 return fBounds.fTop == fBounds.fBottom;
1117 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001118
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001119 bool isVertical() const {
1120 return fBounds.fLeft == fBounds.fRight;
1121 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001122
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001123 // last does not check for done spans because done is only set for the start
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001124 int lastSpan(int end, int step, const SkPoint& startLoc,
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001125 double startT, bool& coincident, bool* manyPtr = NULL) const {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001126 int last = end;
1127 int count = fTs.count();
caryclark@google.com15fa1382012-05-07 20:49:36 +00001128 SkPoint lastLoc;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001129 int found = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001130 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001131 end = last;
1132 if (fTs[end].fCoincident == -step) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001133 coincident = true;
1134 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001135 if (step > 0 ? ++last >= count : --last < 0) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001136 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001137 }
caryclark@google.comfcd4f3e2012-05-07 21:09:32 +00001138 const Span& lastSpan = fTs[last];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001139 if (lastSpan.fT == startT) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001140 ++found;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001141 continue;
1142 }
caryclark@google.comfcd4f3e2012-05-07 21:09:32 +00001143 xyAtT(lastSpan.fT, &lastLoc);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001144 if (startLoc != lastLoc) {
1145 break;
1146 }
1147 ++found;
1148 } while (true);
1149 if (manyPtr) {
1150 *manyPtr = found > 0;
1151 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001152 return end;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001153 }
1154
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001155 SkScalar leftMost(int start, int end) const {
1156 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
1157 }
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001158
1159 void markDone(int index) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001160 SkASSERT(!done());
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001161 double referenceT = fTs[index].fT;
1162 int lesser = index;
1163 while (--lesser >= 0 && referenceT == fTs[lesser].fT) {
1164 SkASSERT(!fTs[lesser].fDone);
1165 fTs[lesser].fDone = true;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001166 fDoneSpans++;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001167 }
1168 do {
1169 SkASSERT(!fTs[index].fDone);
1170 fTs[index].fDone = true;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001171 fDoneSpans++;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001172 } while (++index < fTs.count() && referenceT == fTs[index].fT);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001173 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001174
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001175 // note the assert logic looks for unexpected done of span start
caryclark@google.com15fa1382012-05-07 20:49:36 +00001176 int nextSpan(int from, int step, const SkPoint& fromLoc,
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001177 const Span* fromSpan, SkPoint* toLoc, bool& coincident) const {
1178 coincident = false;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001179 SkASSERT(!done());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001180 int count = fTs.count();
1181 int to = from;
1182 while (step > 0 ? ++to < count : --to >= 0) {
1183 Span* span = &fTs[to];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001184 if (span->fCoincident == step) {
1185 coincident = true;
1186 }
1187 if (fromSpan->fT == span->fT) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001188 continue;
1189 }
1190 SkPoint loc;
1191 xyAtT(span->fT, &loc);
1192 if (fromLoc == loc) {
1193 continue;
1194 }
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001195 SkASSERT(step < 0 || !fTs[from].fDone);
1196 SkASSERT(step > 0 || !span->fDone);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001197 if (toLoc) {
1198 *toLoc = loc;
1199 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001200 return to;
1201 }
1202 return -1;
1203 }
1204
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001205 const SkPoint* pts() const {
1206 return fPts;
1207 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001208
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001209 void reset() {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001210 init(NULL, (SkPath::Verb) -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001211 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1212 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001213 }
1214
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001215 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001216 double t(int tIndex) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001217 SkASSERT(tIndex >= 0);
1218 SkASSERT(tIndex < fTs.count());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001219 return fTs[tIndex].fT;
1220 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001221
1222 void updatePts(const SkPoint pts[]) {
1223 fPts = pts;
1224 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001225
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001226 SkPath::Verb verb() const {
1227 return fVerb;
1228 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001229
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001230 SkScalar xAtT(double t) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001231 SkASSERT(t >= 0 && t <= 1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001232 return (*SegmentXAtT[fVerb])(fPts, t);
1233 }
1234
1235 void xyAtT(double t, SkPoint* pt) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001236 SkASSERT(t >= 0 && t <= 1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001237 (*SegmentXYAtT[fVerb])(fPts, t, pt);
1238 }
1239
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001240 SkScalar yAtT(double t) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001241 SkASSERT(t >= 0 && t <= 1);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001242 return (*SegmentYAtT[fVerb])(fPts, t);
1243 }
1244
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001245#if DEBUG_DUMP
1246 void dump() const {
1247 const char className[] = "Segment";
1248 const int tab = 4;
1249 for (int i = 0; i < fTs.count(); ++i) {
1250 SkPoint out;
1251 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
1252 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001253 " otherT=%1.9g winding=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001254 tab + sizeof(className), className, fID,
1255 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001256 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWinding);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001257 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001258 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001259 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00001260 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001261 }
1262#endif
1263
1264private:
1265 const SkPoint* fPts;
1266 SkPath::Verb fVerb;
1267 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001268 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
1269 // FIXME: coincident only needs two bits (-1, 0, 1)
1270 int fCoincident; // non-zero if some coincident span inside
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001271 int fDoneSpans; // used for quick check that segment is finished
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001272#if DEBUG_DUMP
1273 int fID;
1274#endif
1275};
1276
1277class Contour {
1278public:
1279 Contour() {
1280 reset();
1281#if DEBUG_DUMP
1282 fID = ++gContourID;
1283#endif
1284 }
1285
1286 bool operator<(const Contour& rh) const {
1287 return fBounds.fTop == rh.fBounds.fTop
1288 ? fBounds.fLeft < rh.fBounds.fLeft
1289 : fBounds.fTop < rh.fBounds.fTop;
1290 }
1291
1292 void addCubic(const SkPoint pts[4]) {
1293 fSegments.push_back().addCubic(pts);
1294 fContainsCurves = true;
1295 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001296
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001297 int addLine(const SkPoint pts[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001298 fSegments.push_back().addLine(pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001299 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001300 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001301
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001302 int addQuad(const SkPoint pts[3]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001303 fSegments.push_back().addQuad(pts);
1304 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001305 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001306 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001307
1308 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001309 return fBounds;
1310 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001311
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001312 void complete() {
1313 setBounds();
1314 fContainsIntercepts = false;
1315 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001316
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001317 void containsIntercepts() {
1318 fContainsIntercepts = true;
1319 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001320
1321 void findTooCloseToCall(int winding) {
1322 int segmentCount = fSegments.count();
1323 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1324 fSegments[sIndex].findTooCloseToCall(winding);
1325 }
1326 }
1327
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001328 void fixOtherTIndex() {
1329 int segmentCount = fSegments.count();
1330 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1331 fSegments[sIndex].fixOtherTIndex();
1332 }
1333 }
1334
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001335 void reset() {
1336 fSegments.reset();
1337 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001338 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001339 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001340
1341 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
1342 // we need to sort and walk edges in y, but that on the surface opens the
1343 // same can of worms as before. But then, this is a rough sort based on
1344 // segments' top, and not a true sort, so it could be ameniable to regular
1345 // sorting instead of linear searching. Still feel like I'm missing something
1346 Segment* topSegment() {
1347 int segmentCount = fSegments.count();
1348 SkASSERT(segmentCount > 0);
1349 int best = -1;
1350 Segment* bestSegment = NULL;
1351 while (++best < segmentCount) {
1352 Segment* testSegment = &fSegments[best];
1353 if (testSegment->done()) {
1354 continue;
1355 }
1356 bestSegment = testSegment;
1357 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001358 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001359 if (!bestSegment) {
1360 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001361 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001362 SkScalar bestTop = bestSegment->bounds().fTop;
1363 for (int test = best + 1; test < segmentCount; ++test) {
1364 Segment* testSegment = &fSegments[test];
1365 if (testSegment->done()) {
1366 continue;
1367 }
1368 SkScalar testTop = testSegment->bounds().fTop;
1369 if (bestTop > testTop) {
1370 bestTop = testTop;
1371 bestSegment = testSegment;
1372 }
1373 }
1374 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001375 }
1376
1377#if DEBUG_DUMP
1378 void dump() {
1379 int i;
1380 const char className[] = "Contour";
1381 const int tab = 4;
1382 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
1383 for (i = 0; i < fSegments.count(); ++i) {
1384 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
1385 className, i);
1386 fSegments[i].dump();
1387 }
1388 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
1389 tab + sizeof(className), className,
1390 fBounds.fLeft, fBounds.fTop,
1391 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001392 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
1393 className, fContainsIntercepts);
1394 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
1395 className, fContainsCurves);
1396 }
1397#endif
1398
1399protected:
1400 void setBounds() {
1401 int count = fSegments.count();
1402 if (count == 0) {
1403 SkDebugf("%s empty contour\n", __FUNCTION__);
1404 SkASSERT(0);
1405 // FIXME: delete empty contour?
1406 return;
1407 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001408 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001409 for (int index = 1; index < count; ++index) {
1410 fBounds.growToInclude(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001411 }
1412 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001413
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001414public:
1415 SkTArray<Segment> fSegments; // not worth accessor functions?
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001416
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001417private:
1418 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001419 bool fContainsIntercepts;
1420 bool fContainsCurves;
1421#if DEBUG_DUMP
1422 int fID;
1423#endif
1424};
1425
1426class EdgeBuilder {
1427public:
1428
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001429EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001430 : fPath(path)
1431 , fCurrentContour(NULL)
1432 , fContours(contours)
1433{
1434#if DEBUG_DUMP
1435 gContourID = 0;
1436 gSegmentID = 0;
1437#endif
1438 walk();
1439}
1440
1441protected:
1442
1443void complete() {
1444 if (fCurrentContour && fCurrentContour->fSegments.count()) {
1445 fCurrentContour->complete();
1446 fCurrentContour = NULL;
1447 }
1448}
1449
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001450void walk() {
1451 // FIXME:remove once we can access path pts directly
1452 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
1453 SkPoint pts[4];
1454 SkPath::Verb verb;
1455 do {
1456 verb = iter.next(pts);
1457 *fPathVerbs.append() = verb;
1458 if (verb == SkPath::kMove_Verb) {
1459 *fPathPts.append() = pts[0];
1460 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
1461 fPathPts.append(verb, &pts[1]);
1462 }
1463 } while (verb != SkPath::kDone_Verb);
1464 // FIXME: end of section to remove once path pts are accessed directly
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001465
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001466 SkPath::Verb reducedVerb;
1467 uint8_t* verbPtr = fPathVerbs.begin();
1468 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001469 const SkPoint* finalCurveStart = NULL;
1470 const SkPoint* finalCurveEnd = NULL;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001471 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
1472 switch (verb) {
1473 case SkPath::kMove_Verb:
1474 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001475 if (!fCurrentContour) {
1476 fCurrentContour = fContours.push_back_n(1);
1477 finalCurveEnd = pointsPtr++;
1478 *fExtra.append() = -1; // start new contour
1479 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001480 continue;
1481 case SkPath::kLine_Verb:
1482 // skip degenerate points
1483 if (pointsPtr[-1].fX != pointsPtr[0].fX
1484 || pointsPtr[-1].fY != pointsPtr[0].fY) {
1485 fCurrentContour->addLine(&pointsPtr[-1]);
1486 }
1487 break;
1488 case SkPath::kQuad_Verb:
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001489
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001490 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
1491 if (reducedVerb == 0) {
1492 break; // skip degenerate points
1493 }
1494 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001495 *fExtra.append() =
1496 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001497 break;
1498 }
1499 fCurrentContour->addQuad(&pointsPtr[-1]);
1500 break;
1501 case SkPath::kCubic_Verb:
1502 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
1503 if (reducedVerb == 0) {
1504 break; // skip degenerate points
1505 }
1506 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001507 *fExtra.append() =
1508 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001509 break;
1510 }
1511 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001512 *fExtra.append() =
1513 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001514 break;
1515 }
1516 fCurrentContour->addCubic(&pointsPtr[-1]);
1517 break;
1518 case SkPath::kClose_Verb:
1519 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001520 if (finalCurveStart && finalCurveEnd
1521 && *finalCurveStart != *finalCurveEnd) {
1522 *fReducePts.append() = *finalCurveStart;
1523 *fReducePts.append() = *finalCurveEnd;
1524 *fExtra.append() =
1525 fCurrentContour->addLine(fReducePts.end() - 2);
1526 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001527 complete();
1528 continue;
1529 default:
1530 SkDEBUGFAIL("bad verb");
1531 return;
1532 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001533 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001534 pointsPtr += verb;
1535 SkASSERT(fCurrentContour);
1536 }
1537 complete();
1538 if (fCurrentContour && !fCurrentContour->fSegments.count()) {
1539 fContours.pop_back();
1540 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001541 // correct pointers in contours since fReducePts may have moved as it grew
1542 int cIndex = 0;
1543 fCurrentContour = &fContours[0];
1544 int extraCount = fExtra.count();
1545 SkASSERT(fExtra[0] == -1);
1546 int eIndex = 0;
1547 int rIndex = 0;
1548 while (++eIndex < extraCount) {
1549 int offset = fExtra[eIndex];
1550 if (offset < 0) {
1551 fCurrentContour = &fContours[++cIndex];
1552 continue;
1553 }
1554 Segment& segment = fCurrentContour->fSegments[offset - 1];
1555 segment.updatePts(&fReducePts[rIndex]);
1556 rIndex += segment.verb() + 1;
1557 }
1558 fExtra.reset(); // we're done with this
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001559}
1560
1561private:
1562 const SkPath& fPath;
1563 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001564 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001565 Contour* fCurrentContour;
1566 SkTArray<Contour>& fContours;
1567 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001568 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001569};
1570
1571class Work {
1572public:
1573 enum SegmentType {
1574 kHorizontalLine_Segment = -1,
1575 kVerticalLine_Segment = 0,
1576 kLine_Segment = SkPath::kLine_Verb,
1577 kQuad_Segment = SkPath::kQuad_Verb,
1578 kCubic_Segment = SkPath::kCubic_Verb,
1579 };
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001580
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001581 // FIXME: does it make sense to write otherIndex now if we're going to
1582 // fix it up later?
1583 void addOtherT(int index, double otherT, int otherIndex) {
1584 fContour->fSegments[fIndex].addOtherT(index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001585 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001586
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001587 // Avoid collapsing t values that are close to the same since
1588 // we walk ts to describe consecutive intersections. Since a pair of ts can
1589 // be nearly equal, any problems caused by this should be taken care
1590 // of later.
1591 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com15fa1382012-05-07 20:49:36 +00001592 int addT(double newT, const Work& other, int coincident) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001593 fContour->containsIntercepts();
caryclark@google.com15fa1382012-05-07 20:49:36 +00001594 return fContour->fSegments[fIndex].addT(newT,
1595 other.fContour->fSegments[other.fIndex], coincident);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001596 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001597
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001598 bool advance() {
1599 return ++fIndex < fLast;
1600 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001601
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001602 SkScalar bottom() const {
1603 return bounds().fBottom;
1604 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001605
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001606 const Bounds& bounds() const {
1607 return fContour->fSegments[fIndex].bounds();
1608 }
1609
1610 const SkPoint* cubic() const {
1611 return fCubic;
1612 }
1613
1614 void init(Contour* contour) {
1615 fContour = contour;
1616 fIndex = 0;
1617 fLast = contour->fSegments.count();
1618 }
1619
1620 SkScalar left() const {
1621 return bounds().fLeft;
1622 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001623
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001624 void promoteToCubic() {
1625 fCubic[0] = pts()[0];
1626 fCubic[2] = pts()[1];
1627 fCubic[3] = pts()[2];
1628 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
1629 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
1630 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
1631 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
1632 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001633
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001634 const SkPoint* pts() const {
1635 return fContour->fSegments[fIndex].pts();
1636 }
1637
1638 SkScalar right() const {
1639 return bounds().fRight;
1640 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001641
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001642 ptrdiff_t segmentIndex() const {
1643 return fIndex;
1644 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001645
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001646 SegmentType segmentType() const {
1647 const Segment& segment = fContour->fSegments[fIndex];
1648 SegmentType type = (SegmentType) segment.verb();
1649 if (type != kLine_Segment) {
1650 return type;
1651 }
1652 if (segment.isHorizontal()) {
1653 return kHorizontalLine_Segment;
1654 }
1655 if (segment.isVertical()) {
1656 return kVerticalLine_Segment;
1657 }
1658 return kLine_Segment;
1659 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001660
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001661 bool startAfter(const Work& after) {
1662 fIndex = after.fIndex;
1663 return advance();
1664 }
1665
1666 SkScalar top() const {
1667 return bounds().fTop;
1668 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001669
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001670 SkPath::Verb verb() const {
1671 return fContour->fSegments[fIndex].verb();
1672 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001673
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001674 SkScalar x() const {
1675 return bounds().fLeft;
1676 }
1677
1678 bool xFlipped() const {
1679 return x() != pts()[0].fX;
1680 }
1681
1682 SkScalar y() const {
1683 return bounds().fTop;
1684 }
1685
1686 bool yFlipped() const {
1687 return y() != pts()[0].fX;
1688 }
1689
1690protected:
1691 Contour* fContour;
1692 SkPoint fCubic[4];
1693 int fIndex;
1694 int fLast;
1695};
1696
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001697#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001698static void debugShowLineIntersection(int pts, const Work& wt,
1699 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001700 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001701 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
1702 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
1703 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
1704 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001705 return;
1706 }
1707 SkPoint wtOutPt, wnOutPt;
1708 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
1709 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001710 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001711 __FUNCTION__,
1712 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
1713 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
1714 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001715 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001716 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001717 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001718 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
1719 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
1720 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001721 SkDebugf(" wnTs[1]=%g", wnTs[1]);
1722 SkDebugf("\n");
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001723 }
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001724#else
1725static void debugShowLineIntersection(int , const Work& ,
1726 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001727}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001728#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001729
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001730static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001731
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001732 if (test != next) {
1733 if (test->bounds().fBottom < next->bounds().fTop) {
1734 return false;
1735 }
1736 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
1737 return true;
1738 }
1739 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001740 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001741 wt.init(test);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001742 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001743 Work wn;
1744 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001745 if (test == next && !wn.startAfter(wt)) {
1746 continue;
1747 }
1748 do {
1749 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
1750 continue;
1751 }
1752 int pts;
1753 Intersections ts;
1754 bool swap = false;
1755 switch (wt.segmentType()) {
1756 case Work::kHorizontalLine_Segment:
1757 swap = true;
1758 switch (wn.segmentType()) {
1759 case Work::kHorizontalLine_Segment:
1760 case Work::kVerticalLine_Segment:
1761 case Work::kLine_Segment: {
1762 pts = HLineIntersect(wn.pts(), wt.left(),
1763 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001764 debugShowLineIntersection(pts, wt, wn,
1765 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001766 break;
1767 }
1768 case Work::kQuad_Segment: {
1769 pts = HQuadIntersect(wn.pts(), wt.left(),
1770 wt.right(), wt.y(), wt.xFlipped(), ts);
1771 break;
1772 }
1773 case Work::kCubic_Segment: {
1774 pts = HCubicIntersect(wn.pts(), wt.left(),
1775 wt.right(), wt.y(), wt.xFlipped(), ts);
1776 break;
1777 }
1778 default:
1779 SkASSERT(0);
1780 }
1781 break;
1782 case Work::kVerticalLine_Segment:
1783 swap = true;
1784 switch (wn.segmentType()) {
1785 case Work::kHorizontalLine_Segment:
1786 case Work::kVerticalLine_Segment:
1787 case Work::kLine_Segment: {
1788 pts = VLineIntersect(wn.pts(), wt.top(),
1789 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001790 debugShowLineIntersection(pts, wt, wn,
1791 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001792 break;
1793 }
1794 case Work::kQuad_Segment: {
1795 pts = VQuadIntersect(wn.pts(), wt.top(),
1796 wt.bottom(), wt.x(), wt.yFlipped(), ts);
1797 break;
1798 }
1799 case Work::kCubic_Segment: {
1800 pts = VCubicIntersect(wn.pts(), wt.top(),
1801 wt.bottom(), wt.x(), wt.yFlipped(), ts);
1802 break;
1803 }
1804 default:
1805 SkASSERT(0);
1806 }
1807 break;
1808 case Work::kLine_Segment:
1809 switch (wn.segmentType()) {
1810 case Work::kHorizontalLine_Segment:
1811 pts = HLineIntersect(wt.pts(), wn.left(),
1812 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001813 debugShowLineIntersection(pts, wt, wn,
1814 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001815 break;
1816 case Work::kVerticalLine_Segment:
1817 pts = VLineIntersect(wt.pts(), wn.top(),
1818 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001819 debugShowLineIntersection(pts, wt, wn,
1820 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001821 break;
1822 case Work::kLine_Segment: {
1823 pts = LineIntersect(wt.pts(), wn.pts(), ts);
1824 debugShowLineIntersection(pts, wt, wn,
1825 ts.fT[1], ts.fT[0]);
1826 break;
1827 }
1828 case Work::kQuad_Segment: {
1829 swap = true;
1830 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
1831 break;
1832 }
1833 case Work::kCubic_Segment: {
1834 swap = true;
1835 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
1836 break;
1837 }
1838 default:
1839 SkASSERT(0);
1840 }
1841 break;
1842 case Work::kQuad_Segment:
1843 switch (wn.segmentType()) {
1844 case Work::kHorizontalLine_Segment:
1845 pts = HQuadIntersect(wt.pts(), wn.left(),
1846 wn.right(), wn.y(), wn.xFlipped(), ts);
1847 break;
1848 case Work::kVerticalLine_Segment:
1849 pts = VQuadIntersect(wt.pts(), wn.top(),
1850 wn.bottom(), wn.x(), wn.yFlipped(), ts);
1851 break;
1852 case Work::kLine_Segment: {
1853 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
1854 break;
1855 }
1856 case Work::kQuad_Segment: {
1857 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
1858 break;
1859 }
1860 case Work::kCubic_Segment: {
1861 wt.promoteToCubic();
1862 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
1863 break;
1864 }
1865 default:
1866 SkASSERT(0);
1867 }
1868 break;
1869 case Work::kCubic_Segment:
1870 switch (wn.segmentType()) {
1871 case Work::kHorizontalLine_Segment:
1872 pts = HCubicIntersect(wt.pts(), wn.left(),
1873 wn.right(), wn.y(), wn.xFlipped(), ts);
1874 break;
1875 case Work::kVerticalLine_Segment:
1876 pts = VCubicIntersect(wt.pts(), wn.top(),
1877 wn.bottom(), wn.x(), wn.yFlipped(), ts);
1878 break;
1879 case Work::kLine_Segment: {
1880 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
1881 break;
1882 }
1883 case Work::kQuad_Segment: {
1884 wn.promoteToCubic();
1885 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
1886 break;
1887 }
1888 case Work::kCubic_Segment: {
1889 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
1890 break;
1891 }
1892 default:
1893 SkASSERT(0);
1894 }
1895 break;
1896 default:
1897 SkASSERT(0);
1898 }
1899 // in addition to recording T values, record matching segment
caryclark@google.com15fa1382012-05-07 20:49:36 +00001900 int coincident = pts == 2 && wn.segmentType() <= Work::kLine_Segment
1901 && wt.segmentType() <= Work::kLine_Segment ? -1 :0;
1902 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001903 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
1904 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001905 int testTAt = wt.addT(ts.fT[swap][pt], wn, coincident);
1906 int nextTAt = wn.addT(ts.fT[!swap][pt], wt, coincident);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001907 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
1908 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001909 coincident = -coincident;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001910 }
1911 } while (wn.advance());
1912 } while (wt.advance());
1913 return true;
1914}
1915
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001916// see if coincidence is formed by clipping non-concident segments
1917static void coincidenceCheck(SkTDArray<Contour*>& contourList, int winding) {
1918 int contourCount = contourList.count();
1919 for (size_t cIndex = 0; cIndex < contourCount; ++cIndex) {
1920 Contour* contour = contourList[cIndex];
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001921 contour->findTooCloseToCall(winding);
1922 }
1923}
1924
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001925
1926// OPTIMIZATION: not crazy about linear search here to find top active y.
1927// seems like we should break down and do the sort, or maybe sort each
1928// contours' segments?
1929// Once the segment array is built, there's no reason I can think of not to
1930// sort it in Y. hmmm
1931static Segment* findTopContour(SkTDArray<Contour*>& contourList,
1932 int contourCount) {
1933 int cIndex = 0;
1934 Segment* topStart;
1935 do {
1936 Contour* topContour = contourList[cIndex];
1937 topStart = topContour->topSegment();
1938 } while (!topStart && ++cIndex < contourCount);
1939 if (!topStart) {
1940 return NULL;
1941 }
1942 SkScalar top = topStart->bounds().fTop;
1943 for (int cTest = cIndex + 1; cTest < contourCount; ++cTest) {
1944 Contour* contour = contourList[cTest];
1945 if (top < contour->bounds().fTop) {
1946 continue;
1947 }
1948 Segment* test = contour->topSegment();
1949 if (top > test->bounds().fTop) {
1950 cIndex = cTest;
1951 topStart = test;
1952 top = test->bounds().fTop;
1953 }
1954 }
1955 return topStart;
1956}
1957
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001958// Each segment may have an inside or an outside. Segments contained within
1959// winding may have insides on either side, and form a contour that should be
1960// ignored. Segments that are coincident with opposing direction segments may
1961// have outsides on either side, and should also disappear.
1962// 'Normal' segments will have one inside and one outside. Subsequent connections
1963// when winding should follow the intersection direction. If more than one edge
1964// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001965 // since we start with leftmost top edge, we'll traverse through a
1966 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001967static void bridge(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001968 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001969 int winding = 0; // there are no contours outside this one
caryclark@google.com15fa1382012-05-07 20:49:36 +00001970 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001971 Segment* topStart = findTopContour(contourList, contourCount);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001972 if (!topStart) {
1973 break;
1974 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001975 // Start at the top. Above the top is outside, below is inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001976 // follow edges to intersection by changing the tIndex by direction.
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001977 int tIndex, endIndex;
1978 Segment* topSegment = topStart->findTop(tIndex, endIndex);
1979 Segment* next = topSegment;
1980 next->addMoveTo(tIndex, simple);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001981 do {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001982 SkASSERT(!next->done());
1983 next->addCurveTo(tIndex, endIndex, simple);
1984 next = next->findNext(winding, tIndex, endIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001985 } while (next != topSegment);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001986 #if DEBUG_PATH_CONSTRUCTION
1987 SkDebugf("%s close\n", __FUNCTION__);
1988 #endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001989 simple.close();
1990 } while (true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001991
caryclark@google.com15fa1382012-05-07 20:49:36 +00001992 // at intersection, stay on outside, but mark remaining edges as inside
1993 // or, only mark first pair as inside?
1994 // how is this going to work for contained (but not intersecting)
1995 // segments?
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001996 // start here ;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001997 // find span
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001998 // mark neighbors winding coverage
1999 // output span
2000 // mark span as processed
caryclark@google.com15fa1382012-05-07 20:49:36 +00002001
caryclark@google.com15fa1382012-05-07 20:49:36 +00002002
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002003
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002004}
2005
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002006static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
2007 int contourCount = contourList.count();
2008 for (int cTest = 0; cTest < contourCount; ++cTest) {
2009 Contour* contour = contourList[cTest];
2010 contour->fixOtherTIndex();
2011 }
2012}
2013
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002014static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002015 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002016 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002017 if (count == 0) {
2018 return;
2019 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002020 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002021 *list.append() = &contours[index];
2022 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002023 QSort<Contour>(list.begin(), list.end() - 1);
2024}
2025
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002026void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002027 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002028 int winding = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002029 simple.reset();
2030 simple.setFillType(SkPath::kEvenOdd_FillType);
2031
2032 // turn path into list of segments
2033 SkTArray<Contour> contours;
2034 // FIXME: add self-intersecting cubics' T values to segment
2035 EdgeBuilder builder(path, contours);
2036 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002037 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002038 Contour** currentPtr = contourList.begin();
2039 if (!currentPtr) {
2040 return;
2041 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002042 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002043 // find all intersections between segments
2044 do {
2045 Contour** nextPtr = currentPtr;
2046 Contour* current = *currentPtr++;
2047 Contour* next;
2048 do {
2049 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002050 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002051 } while (currentPtr != listEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002052 fixOtherTIndex(contourList);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002053 // eat through coincident edges
2054 coincidenceCheck(contourList, winding);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002055 // construct closed contours
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002056 bridge(contourList, simple);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002057}
2058