blob: 1fd818558bc34b2e3adfe3bddec3c2c5f79ae5b8 [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
caryclark@google.com8dcf1142012-07-02 20:27:02 +000028#define DEBUG_CROSS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000029#define DEBUG_DUMP 0
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000030#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com027de222012-07-12 12:52:50 +000031#define DEBUG_ACTIVE_SPANS 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000032#define DEBUG_WINDING 0
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000033#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.com8dcf1142012-07-02 20:27:02 +000034#define DEBUG_MARK_DONE 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000035
36#else
37
38//const bool gRunTestsInOneThread = true;
39
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000040#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000041#define DEBUG_BRIDGE 1
caryclark@google.com8dcf1142012-07-02 20:27:02 +000042#define DEBUG_CROSS 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000043#define DEBUG_DUMP 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000044#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com210acaf2012-07-12 21:05:13 +000045#define DEBUG_ACTIVE_SPANS 01
46#define DEBUG_WINDING 01
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000047#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.com210acaf2012-07-12 21:05:13 +000048#define DEBUG_MARK_DONE 01
caryclark@google.comfa0588f2012-04-26 21:01:06 +000049
50#endif
51
caryclark@google.com027de222012-07-12 12:52:50 +000052#if DEBUG_ACTIVE_SPANS && !DEBUG_DUMP
53#undef DEBUG_DUMP
54#define DEBUG_DUMP 1
55#endif
56
caryclark@google.comfa0588f2012-04-26 21:01:06 +000057#if DEBUG_DUMP
58static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000059// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000060static int gContourID;
61static int gSegmentID;
62#endif
63
caryclark@google.com8dcf1142012-07-02 20:27:02 +000064#ifndef DEBUG_TEST
65#define DEBUG_TEST 0
66#endif
67
caryclark@google.comfa0588f2012-04-26 21:01:06 +000068static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
69 Intersections& intersections) {
70 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
71 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
72 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
73}
74
75static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
76 Intersections& intersections) {
77 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
78 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
79 intersect(aQuad, bLine, intersections);
80 return intersections.fUsed;
81}
82
83static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
84 Intersections& intersections) {
85 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
86 {a[3].fX, a[3].fY}};
87 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
88 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
89}
90
91static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
92 Intersections& intersections) {
93 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
94 const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
95 intersect(aQuad, bQuad, intersections);
96 return intersections.fUsed;
97}
98
99static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
100 Intersections& intersections) {
101 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
102 {a[3].fX, a[3].fY}};
103 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
104 {b[3].fX, b[3].fY}};
105 intersect(aCubic, bCubic, intersections);
106 return intersections.fUsed;
107}
108
109static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
110 SkScalar y, bool flipped, Intersections& intersections) {
111 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
112 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
113}
114
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000115static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
116 SkScalar y, bool flipped, Intersections& intersections) {
117 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
118 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
119}
120
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000121static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
122 SkScalar y, bool flipped, Intersections& intersections) {
123 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
124 {a[3].fX, a[3].fY}};
125 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
126}
127
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000128static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
129 SkScalar x, bool flipped, Intersections& intersections) {
130 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
131 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
132}
133
134static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
135 SkScalar x, bool flipped, Intersections& intersections) {
136 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
137 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
138}
139
140static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
141 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000142 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
143 {a[3].fX, a[3].fY}};
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000144 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000145}
146
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000147static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
148 SkScalar , SkScalar , bool , Intersections& ) = {
149 NULL,
150 VLineIntersect,
151 VQuadIntersect,
152 VCubicIntersect
153};
154
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000155static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
156 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
157 double x, y;
158 xy_at_t(line, t, x, y);
159 out->fX = SkDoubleToScalar(x);
160 out->fY = SkDoubleToScalar(y);
161}
162
163static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
164 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
165 double x, y;
166 xy_at_t(quad, t, x, y);
167 out->fX = SkDoubleToScalar(x);
168 out->fY = SkDoubleToScalar(y);
169}
170
171static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
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 x, y;
175 xy_at_t(cubic, t, x, y);
176 out->fX = SkDoubleToScalar(x);
177 out->fY = SkDoubleToScalar(y);
178}
179
180static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
181 NULL,
182 LineXYAtT,
183 QuadXYAtT,
184 CubicXYAtT
185};
186
187static SkScalar LineXAtT(const SkPoint a[2], double t) {
188 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
189 double x;
190 xy_at_t(aLine, t, x, *(double*) 0);
191 return SkDoubleToScalar(x);
192}
193
194static SkScalar QuadXAtT(const SkPoint a[3], double t) {
195 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
196 double x;
197 xy_at_t(quad, t, x, *(double*) 0);
198 return SkDoubleToScalar(x);
199}
200
201static SkScalar CubicXAtT(const SkPoint a[4], double t) {
202 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
203 {a[3].fX, a[3].fY}};
204 double x;
205 xy_at_t(cubic, t, x, *(double*) 0);
206 return SkDoubleToScalar(x);
207}
208
209static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
210 NULL,
211 LineXAtT,
212 QuadXAtT,
213 CubicXAtT
214};
215
216static SkScalar LineYAtT(const SkPoint a[2], double t) {
217 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
218 double y;
219 xy_at_t(aLine, t, *(double*) 0, y);
220 return SkDoubleToScalar(y);
221}
222
223static SkScalar QuadYAtT(const SkPoint a[3], double t) {
224 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
225 double y;
226 xy_at_t(quad, t, *(double*) 0, y);
227 return SkDoubleToScalar(y);
228}
229
230static SkScalar CubicYAtT(const SkPoint a[4], double t) {
231 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
232 {a[3].fX, a[3].fY}};
233 double y;
234 xy_at_t(cubic, t, *(double*) 0, y);
235 return SkDoubleToScalar(y);
236}
237
238static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
239 NULL,
240 LineYAtT,
241 QuadYAtT,
242 CubicYAtT
243};
244
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000245static SkScalar LineDXAtT(const SkPoint a[2], double ) {
246 return a[1].fX - a[0].fX;
247}
248
249static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
250 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
251 double x;
252 dxdy_at_t(quad, t, x, *(double*) 0);
253 return SkDoubleToScalar(x);
254}
255
256static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
257 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
258 {a[3].fX, a[3].fY}};
259 double x;
260 dxdy_at_t(cubic, t, x, *(double*) 0);
261 return SkDoubleToScalar(x);
262}
263
264static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
265 NULL,
266 LineDXAtT,
267 QuadDXAtT,
268 CubicDXAtT
269};
270
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000271static void LineSubDivide(const SkPoint a[2], double startT, double endT,
272 SkPoint sub[2]) {
273 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
274 _Line dst;
275 sub_divide(aLine, startT, endT, dst);
276 sub[0].fX = SkDoubleToScalar(dst[0].x);
277 sub[0].fY = SkDoubleToScalar(dst[0].y);
278 sub[1].fX = SkDoubleToScalar(dst[1].x);
279 sub[1].fY = SkDoubleToScalar(dst[1].y);
280}
281
282static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
283 SkPoint sub[3]) {
284 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
285 {a[2].fX, a[2].fY}};
286 Quadratic dst;
287 sub_divide(aQuad, startT, endT, dst);
288 sub[0].fX = SkDoubleToScalar(dst[0].x);
289 sub[0].fY = SkDoubleToScalar(dst[0].y);
290 sub[1].fX = SkDoubleToScalar(dst[1].x);
291 sub[1].fY = SkDoubleToScalar(dst[1].y);
292 sub[2].fX = SkDoubleToScalar(dst[2].x);
293 sub[2].fY = SkDoubleToScalar(dst[2].y);
294}
295
296static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
297 SkPoint sub[4]) {
298 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
299 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
300 Cubic dst;
301 sub_divide(aCubic, startT, endT, dst);
302 sub[0].fX = SkDoubleToScalar(dst[0].x);
303 sub[0].fY = SkDoubleToScalar(dst[0].y);
304 sub[1].fX = SkDoubleToScalar(dst[1].x);
305 sub[1].fY = SkDoubleToScalar(dst[1].y);
306 sub[2].fX = SkDoubleToScalar(dst[2].x);
307 sub[2].fY = SkDoubleToScalar(dst[2].y);
308 sub[3].fX = SkDoubleToScalar(dst[3].x);
309 sub[3].fY = SkDoubleToScalar(dst[3].y);
310}
311
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000312static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
313 SkPoint []) = {
314 NULL,
315 LineSubDivide,
316 QuadSubDivide,
317 CubicSubDivide
318};
319
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000320#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000321static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
322 SkRect& bounds) {
323 SkPoint dst[3];
324 QuadSubDivide(a, startT, endT, dst);
325 bounds.fLeft = bounds.fRight = dst[0].fX;
326 bounds.fTop = bounds.fBottom = dst[0].fY;
327 for (int index = 1; index < 3; ++index) {
328 bounds.growToInclude(dst[index].fX, dst[index].fY);
329 }
330}
331
332static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
333 SkRect& bounds) {
334 SkPoint dst[4];
335 CubicSubDivide(a, startT, endT, dst);
336 bounds.fLeft = bounds.fRight = dst[0].fX;
337 bounds.fTop = bounds.fBottom = dst[0].fY;
338 for (int index = 1; index < 4; ++index) {
339 bounds.growToInclude(dst[index].fX, dst[index].fY);
340 }
341}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000342#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000343
caryclark@google.com15fa1382012-05-07 20:49:36 +0000344static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000345 SkTDArray<SkPoint>& reducePts) {
346 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
347 {a[2].fX, a[2].fY}};
348 Quadratic dst;
349 int order = reduceOrder(aQuad, dst);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000350 if (order == 3) {
351 return SkPath::kQuad_Verb;
352 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000353 for (int index = 0; index < order; ++index) {
354 SkPoint* pt = reducePts.append();
355 pt->fX = SkDoubleToScalar(dst[index].x);
356 pt->fY = SkDoubleToScalar(dst[index].y);
357 }
358 return (SkPath::Verb) (order - 1);
359}
360
361static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
362 SkTDArray<SkPoint>& reducePts) {
363 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
364 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
365 Cubic dst;
366 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000367 if (order == 4) {
368 return SkPath::kCubic_Verb;
369 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000370 for (int index = 0; index < order; ++index) {
371 SkPoint* pt = reducePts.append();
372 pt->fX = SkDoubleToScalar(dst[index].x);
373 pt->fY = SkDoubleToScalar(dst[index].y);
374 }
375 return (SkPath::Verb) (order - 1);
376}
377
caryclark@google.com15fa1382012-05-07 20:49:36 +0000378static bool QuadIsLinear(const SkPoint a[3]) {
379 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
380 {a[2].fX, a[2].fY}};
381 return isLinear(aQuad, 0, 2);
382}
383
384static bool CubicIsLinear(const SkPoint a[4]) {
385 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
386 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
387 return isLinear(aCubic, 0, 3);
388}
389
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000390static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
391 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
392 double x[2];
393 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000394 xy_at_t(aLine, endT, x[1], *(double*) 0);
395 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000396}
397
398static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
399 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
400 {a[2].fX, a[2].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000401 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000402}
403
404static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
405 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
406 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000407 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000408}
409
410static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
411 NULL,
412 LineLeftMost,
413 QuadLeftMost,
414 CubicLeftMost
415};
416
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000417#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000418static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
419 const SkPoint& below) {
420 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
421 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
422 return implicit_matches_ulps(aLine, bLine, 32);
423}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000424#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000425
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000426class Segment;
427
caryclark@google.com15fa1382012-05-07 20:49:36 +0000428// sorting angles
429// given angles of {dx dy ddx ddy dddx dddy} sort them
430class Angle {
431public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000432 // FIXME: this is bogus for quads and cubics
433 // if the quads and cubics' line from end pt to ctrl pt are coincident,
434 // there's no obvious way to determine the curve ordering from the
435 // derivatives alone. In particular, if one quadratic's coincident tangent
436 // is longer than the other curve, the final control point can place the
437 // longer curve on either side of the shorter one.
438 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
439 // may provide some help, but nothing has been figured out yet.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000440 bool operator<(const Angle& rh) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000441 if ((fDy < 0) ^ (rh.fDy < 0)) {
442 return fDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000443 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000444 if (fDy == 0 && rh.fDy == 0 && fDx != rh.fDx) {
445 return fDx < rh.fDx;
446 }
447 SkScalar cmp = fDx * rh.fDy - rh.fDx * fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000448 if (cmp) {
449 return cmp < 0;
450 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000451 if ((fDDy < 0) ^ (rh.fDDy < 0)) {
452 return fDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000453 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000454 if (fDDy == 0 && rh.fDDy == 0 && fDDx != rh.fDDx) {
455 return fDDx < rh.fDDx;
456 }
457 cmp = fDDx * rh.fDDy - rh.fDDx * fDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000458 if (cmp) {
459 return cmp < 0;
460 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000461 if ((fDDDy < 0) ^ (rh.fDDDy < 0)) {
462 return fDDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000463 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000464 if (fDDDy == 0 && rh.fDDDy == 0) {
465 return fDDDx < rh.fDDDx;
466 }
467 return fDDDx * rh.fDDDy < rh.fDDDx * fDDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000468 }
469
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000470 int end() const {
471 return fEnd;
472 }
473
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000474 bool isHorizontal() const {
475 return fDy == 0 && fDDy == 0 && fDDDy == 0;
476 }
477
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000478 // since all angles share a point, this needs to know which point
479 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
480 // practically, this should only be called by addAngle
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000481 void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000482 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000483 SkASSERT(start != end);
484 fSegment = segment;
485 fStart = start;
486 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000487 fDx = pts[1].fX - pts[0].fX; // b - a
488 fDy = pts[1].fY - pts[0].fY;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000489 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000490 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000491 return;
492 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000493 fDDx = pts[2].fX - pts[1].fX - fDx; // a - 2b + c
494 fDDy = pts[2].fY - pts[1].fY - fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000495 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000496 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000497 return;
498 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000499 fDDDx = pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX;
500 fDDDy = pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY;
501 }
502
503 // noncoincident quads/cubics may have the same initial angle
504 // as lines, so must sort by derivatives as well
505 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000506 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000507 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000508 fSegment = segment;
509 fStart = start;
510 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000511 fDx = pts[1].fX - pts[0].fX; // b - a
512 fDy = pts[1].fY - pts[0].fY;
513 if (verb == SkPath::kLine_Verb) {
514 fDDx = fDDy = fDDDx = fDDDy = 0;
515 return;
516 }
517 if (verb == SkPath::kQuad_Verb) {
518 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
519 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
520 int larger = std::max(abs(uplsX), abs(uplsY));
521 int shift = 0;
522 double flatT;
523 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
524 LineParameters implicitLine;
525 _Line tangent = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}};
526 implicitLine.lineEndPoints(tangent);
527 implicitLine.normalize();
528 while (larger > UlpsEpsilon * 1024) {
529 larger >>= 2;
530 ++shift;
531 flatT = 0.5 / (1 << shift);
532 QuadXYAtT(pts, flatT, &ddPt);
533 _Point _pt = {ddPt.fX, ddPt.fY};
534 double distance = implicitLine.pointDistance(_pt);
535 if (approximately_zero(distance)) {
536 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
537 break;
538 }
539 }
540 flatT = 0.5 / (1 << shift);
541 QuadXYAtT(pts, flatT, &ddPt);
542 fDDx = ddPt.fX - pts[0].fX;
543 fDDy = ddPt.fY - pts[0].fY;
544 SkASSERT(fDDx != 0 || fDDy != 0);
545 fDDDx = fDDDy = 0;
546 return;
547 }
548 SkASSERT(0); // FIXME: add cubic case
549 }
550
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000551 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000552 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000553 }
554
555 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000556 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000557 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000558
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000559 int start() const {
560 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000561 }
562
563private:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000564 SkScalar fDx;
565 SkScalar fDy;
566 SkScalar fDDx;
567 SkScalar fDDy;
568 SkScalar fDDDx;
569 SkScalar fDDDy;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000570 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000571 int fStart;
572 int fEnd;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000573};
574
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000575static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
576 int angleCount = angles.count();
577 int angleIndex;
578 angleList.setReserve(angleCount);
579 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
580 *angleList.append() = &angles[angleIndex];
581 }
582 QSort<Angle>(angleList.begin(), angleList.end() - 1);
583}
584
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000585// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000586struct Bounds : public SkRect {
587 static bool Intersects(const Bounds& a, const Bounds& b) {
588 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
589 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
590 }
591
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000592 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
593 if (left < fLeft) {
594 fLeft = left;
595 }
596 if (top < fTop) {
597 fTop = top;
598 }
599 if (right > fRight) {
600 fRight = right;
601 }
602 if (bottom > fBottom) {
603 fBottom = bottom;
604 }
605 }
606
607 void add(const Bounds& toAdd) {
608 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
609 }
610
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000611 bool isEmpty() {
612 return fLeft > fRight || fTop > fBottom
613 || fLeft == fRight && fTop == fBottom
614 || isnan(fLeft) || isnan(fRight)
615 || isnan(fTop) || isnan(fBottom);
616 }
617
618 void setCubicBounds(const SkPoint a[4]) {
619 _Rect dRect;
620 Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
621 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
622 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000623 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
624 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000625 }
626
627 void setQuadBounds(const SkPoint a[3]) {
628 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
629 {a[2].fX, a[2].fY}};
630 _Rect dRect;
631 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000632 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
633 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000634 }
635};
636
caryclark@google.com15fa1382012-05-07 20:49:36 +0000637struct Span {
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000638 Segment* fOther;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000639 mutable SkPoint const* fPt; // lazily computed as needed
640 double fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000641 double fOtherT; // value at fOther[fOtherIndex].fT
642 int fOtherIndex; // can't be used during intersection
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000643 int fWindSum; // accumulated from contours surrounding this one
644 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000645 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000646};
647
648class Segment {
649public:
650 Segment() {
651#if DEBUG_DUMP
652 fID = ++gSegmentID;
653#endif
654 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000655
caryclark@google.com9764cc62012-07-12 19:29:45 +0000656 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
657 if (activeAngleInner(index, done, angles)) {
658 return true;
659 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000660 double referenceT = fTs[index].fT;
661 int lesser = index;
662 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000663 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000664 return true;
665 }
666 }
667 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000668 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000669 return true;
670 }
671 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
672 return false;
673 }
674
caryclark@google.com9764cc62012-07-12 19:29:45 +0000675 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000676 Span* span = &fTs[index];
677 Segment* other = span->fOther;
678 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +0000679 return other->activeAngleInner(oIndex, done, angles);
680 }
681
682 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
683 int next = nextSpan(index, 1);
684 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000685 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000686 if (upSpan.fWindValue) {
687 addAngle(angles, index, next);
688 if (upSpan.fDone) {
689 done++;
690 } else if (upSpan.fWindSum != SK_MinS32) {
691 return true;
692 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000693 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000694 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000695 int prev = nextSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000696 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +0000697 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000698 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000699 if (downSpan.fWindValue) {
700 addAngle(angles, index, prev);
701 if (downSpan.fDone) {
702 done++;
703 } else if (downSpan.fWindSum != SK_MinS32) {
704 return true;
705 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000706 }
707 }
708 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000709 }
710
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000711 SkScalar activeTop() const {
712 SkASSERT(!done());
713 int count = fTs.count();
714 SkScalar result = SK_ScalarMax;
715 bool lastDone = true;
716 for (int index = 0; index < count; ++index) {
717 bool done = fTs[index].fDone;
718 if (!done || !lastDone) {
719 SkScalar y = yAtT(index);
720 if (result > y) {
721 result = y;
722 }
723 }
724 lastDone = done;
725 }
726 SkASSERT(result < SK_ScalarMax);
727 return result;
728 }
729
730 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000731 SkASSERT(start != end);
732 SkPoint edge[4];
733 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
734 Angle* angle = angles.append();
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000735 angle->set(edge, fVerb, this, start, end);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000736 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000737
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000738 void addCubic(const SkPoint pts[4]) {
739 init(pts, SkPath::kCubic_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000740 fBounds.setCubicBounds(pts);
741 }
742
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000743 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000744 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000745 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000746 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000747 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000748 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000749 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000750 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
751 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
752 if (fVerb > 1) {
753 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
754 }
755 if (fVerb > 2) {
756 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
757 }
758 SkDebugf("\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000759 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000760 switch (fVerb) {
761 case SkPath::kLine_Verb:
762 path.lineTo(edge[1].fX, edge[1].fY);
763 break;
764 case SkPath::kQuad_Verb:
765 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
766 break;
767 case SkPath::kCubic_Verb:
768 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
769 edge[3].fX, edge[3].fY);
770 break;
771 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000772 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000773 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000774 }
775
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000776 void addLine(const SkPoint pts[2]) {
777 init(pts, SkPath::kLine_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000778 fBounds.set(pts, 2);
779 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000780
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000781 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
782 const SkPoint& pt = xyAtT(tIndex);
783 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000784 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000785 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000786 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000787 path.moveTo(pt.fX, pt.fY);
788 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000789 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000790 }
791
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000792 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000793 void addOtherT(int index, double otherT, int otherIndex) {
794 Span& span = fTs[index];
795 span.fOtherT = otherT;
796 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000797 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000798
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000799 void addQuad(const SkPoint pts[3]) {
800 init(pts, SkPath::kQuad_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000801 fBounds.setQuadBounds(pts);
802 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000803
804 // Defer all coincident edge processing until
805 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000806
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000807// no need to be tricky; insert in normal T order
808// resolve overlapping ts when considering coincidence later
809
810 // add non-coincident intersection. Resulting edges are sorted in T.
811 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000812 // FIXME: in the pathological case where there is a ton of intercepts,
813 // binary search?
814 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000815 size_t tCount = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000816 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000817 // OPTIMIZATION: if there are three or more identical Ts, then
818 // the fourth and following could be further insertion-sorted so
819 // that all the edges are clockwise or counterclockwise.
820 // This could later limit segment tests to the two adjacent
821 // neighbors, although it doesn't help with determining which
822 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000823 if (newT < fTs[index].fT) {
824 insertedAt = index;
825 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000826 }
827 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000828 Span* span;
829 if (insertedAt >= 0) {
830 span = fTs.insert(insertedAt);
831 } else {
832 insertedAt = tCount;
833 span = fTs.append();
834 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000835 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000836 span->fOther = other;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000837 span->fPt = NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000838 span->fWindSum = SK_MinS32;
839 span->fWindValue = 1;
840 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000841 ++fDoneSpans;
842 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000843 return insertedAt;
844 }
845
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000846 // set spans from start to end to decrement by one
847 // note this walks other backwards
848 // FIMXE: there's probably an edge case that can be constructed where
849 // two span in one segment are separated by float epsilon on one span but
850 // not the other, if one segment is very small. For this
851 // case the counts asserted below may or may not be enough to separate the
852 // spans. Even if the counts work out, what if the spanw aren't correctly
853 // sorted? It feels better in such a case to match the span's other span
854 // pointer since both coincident segments must contain the same spans.
855 void addTCancel(double startT, double endT, Segment& other,
856 double oStartT, double oEndT) {
857 SkASSERT(endT - startT >= FLT_EPSILON);
858 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
859 int index = 0;
860 while (startT - fTs[index].fT >= FLT_EPSILON) {
861 ++index;
862 }
caryclark@google.comb9738012012-07-03 19:53:30 +0000863 int oIndex = other.fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000864 while (other.fTs[--oIndex].fT - oEndT > -FLT_EPSILON)
865 ;
866 Span* test = &fTs[index];
867 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000868 do {
869 bool decrement = test->fWindValue && oTest->fWindValue;
870 Span* end = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000871 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000872 if (decrement) {
caryclark@google.comb9738012012-07-03 19:53:30 +0000873 SkASSERT(end->fWindValue > 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000874 if (--(end->fWindValue) == 0) {
875 end->fDone = true;
876 ++fDoneSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000877 }
878 }
879 end = &fTs[++index];
880 } while (end->fT - test->fT < FLT_EPSILON);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000881 Span* oTestStart = oTest;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000882 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000883 if (decrement) {
caryclark@google.comb9738012012-07-03 19:53:30 +0000884 SkASSERT(oTestStart->fWindValue > 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000885 if (--(oTestStart->fWindValue) == 0) {
886 oTestStart->fDone = true;
887 ++other.fDoneSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000888 }
889 }
890 if (!oIndex) {
891 break;
892 }
893 oTestStart = &other.fTs[--oIndex];
894 } while (oTest->fT - oTestStart->fT < FLT_EPSILON);
895 test = end;
896 oTest = oTestStart;
897 } while (test->fT < endT - FLT_EPSILON);
898 SkASSERT(!oIndex || oTest->fT <= oStartT - FLT_EPSILON);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000899 }
900
901 // set spans from start to end to increment the greater by one and decrement
902 // the lesser
903 void addTCoincident(double startT, double endT, Segment& other,
904 double oStartT, double oEndT) {
905 SkASSERT(endT - startT >= FLT_EPSILON);
906 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
907 int index = 0;
908 while (startT - fTs[index].fT >= FLT_EPSILON) {
909 ++index;
910 }
911 int oIndex = 0;
912 while (oStartT - other.fTs[oIndex].fT >= FLT_EPSILON) {
913 ++oIndex;
914 }
915 Span* test = &fTs[index];
916 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000917 SkTDArray<double> outsideTs;
918 SkTDArray<double> oOutsideTs;
919 do {
caryclark@google.comb9738012012-07-03 19:53:30 +0000920 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000921 bool decrementOther = test->fWindValue >= oTest->fWindValue;
922 Span* end = test;
923 double startT = end->fT;
924 double oStartT = oTest->fT;
925 do {
caryclark@google.comb9738012012-07-03 19:53:30 +0000926 if (transfer) {
927 if (decrementOther) {
928 ++(end->fWindValue);
929 } else {
930 SkASSERT(end->fWindValue > 0);
931 if (--(end->fWindValue) == 0) {
932 end->fDone = true;
933 ++fDoneSpans;
934 int outCount = outsideTs.count();
935 if (outCount == 0 || end->fT - outsideTs[outCount - 2]
936 >= FLT_EPSILON) {
937 *outsideTs.append() = end->fT;
938 *outsideTs.append() = oStartT;
939 }
940 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000941 }
942 }
943 end = &fTs[++index];
944 } while (end->fT - test->fT < FLT_EPSILON);
945 Span* oEnd = oTest;
946 do {
caryclark@google.comb9738012012-07-03 19:53:30 +0000947 if (transfer) {
948 if (decrementOther) {
949 SkASSERT(oEnd->fWindValue > 0);
950 if (--(oEnd->fWindValue) == 0) {
951 oEnd->fDone = true;
952 ++other.fDoneSpans;
953 int oOutCount = oOutsideTs.count();
954 if (oOutCount == 0 || oEnd->fT
955 - oOutsideTs[oOutCount - 2] >= FLT_EPSILON) {
956 *oOutsideTs.append() = oEnd->fT;
957 *oOutsideTs.append() = startT;
958 }
959 }
960 } else {
961 ++(oEnd->fWindValue);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000962 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000963 }
964 oEnd = &other.fTs[++oIndex];
965 } while (oEnd->fT - oTest->fT < FLT_EPSILON);
966 test = end;
967 oTest = oEnd;
968 } while (test->fT < endT - FLT_EPSILON);
969 SkASSERT(oTest->fT < oEndT + FLT_EPSILON);
970 SkASSERT(oTest->fT > oEndT - FLT_EPSILON);
971 if (!done() && outsideTs.count()) {
caryclark@google.comb9738012012-07-03 19:53:30 +0000972 addTOutsides(outsideTs, other, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000973 }
974 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comb9738012012-07-03 19:53:30 +0000975 other.addTOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000976 }
977 }
978
caryclark@google.comb9738012012-07-03 19:53:30 +0000979 void addTOutsides(const SkTDArray<double>& outsideTs, Segment& other,
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000980 double otherEnd) {
981 int count = outsideTs.count();
982 double endT = 0;
983 int endSpan = 0;
984 for (int index = 0; index < count; index += 2) {
985 double t = outsideTs[index];
986 double otherT = outsideTs[index + 1];
987 if (t > 1 - FLT_EPSILON) {
988 return;
989 }
990 if (t - endT > FLT_EPSILON) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000991 endSpan = addTDonePair(t, other, otherT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000992 }
993 do {
994 endT = fTs[++endSpan].fT;
995 } while (endT - t < FLT_EPSILON);
996 }
997 addTPair(endT, other, otherEnd);
998 }
999
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001000 // match the other.fWindValue to its mates
1001 int addTDonePair(double t, Segment& other, double otherT) {
1002 int insertedAt = addTPair(t, other, otherT);
1003 Span& end = fTs[insertedAt];
1004 SkASSERT(end.fWindValue == 1);
1005 end.fWindValue = 0;
1006 end.fDone = true;
1007 ++fDoneSpans;
1008 Span& otherEnd = other.fTs[end.fOtherIndex];
1009 Span* match = NULL;
1010 if (end.fOtherIndex > 0) {
1011 match = &other.fTs[end.fOtherIndex - 1];
1012 }
1013 if (!match || match->fT < otherT) {
1014 match = &other.fTs[end.fOtherIndex + 1];
1015 }
1016 otherEnd.fWindValue = match->fWindValue;
1017 return insertedAt;
1018 }
1019
caryclark@google.comb9738012012-07-03 19:53:30 +00001020 int addTPair(double t, Segment& other, double otherT) {
1021 int insertedAt = addT(t, &other);
1022 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001023 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001024 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001025 return insertedAt;
1026 }
1027
1028 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001029 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001030 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1031 addAngle(angles, end, start);
1032 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001033 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001034 int step = SkSign32(end - start);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001035 int tIndex = nextSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001036 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001037 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001038 }
1039 }
1040
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001041 const Bounds& bounds() const {
1042 return fBounds;
1043 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001044
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001045 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001046 double referenceT = fTs[index].fT;
1047 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001048 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001049 buildAnglesInner(lesser, angles);
1050 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001051 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001052 buildAnglesInner(index, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001053 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001054 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001055
1056 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1057 Span* span = &fTs[index];
1058 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001059 // if there is only one live crossing, and no coincidence, continue
1060 // in the same direction
1061 // if there is coincidence, the only choice may be to reverse direction
1062 // find edge on either side of intersection
1063 int oIndex = span->fOtherIndex;
1064 // if done == -1, prior span has already been processed
1065 int step = 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001066 int next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001067 if (next < 0) {
1068 step = -step;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001069 next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001070 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001071 // add candidate into and away from junction
1072 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001073 }
1074
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001075 bool cancels(const Segment& other) const {
caryclark@google.comb9738012012-07-03 19:53:30 +00001076 SkASSERT(fVerb == SkPath::kLine_Verb);
1077 SkASSERT(other.fVerb == SkPath::kLine_Verb);
1078 SkPoint dxy = fPts[0] - fPts[1];
1079 SkPoint odxy = other.fPts[0] - other.fPts[1];
1080 return dxy.fX * odxy.fX < 0 || dxy.fY * odxy.fY < 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001081 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001082
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001083 // figure out if the segment's ascending T goes clockwise or not
1084 // not enough context to write this as shown
1085 // instead, add all segments meeting at the top
1086 // sort them using buildAngleList
1087 // find the first in the sort
1088 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001089 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001090 SkASSERT(0); // incomplete
1091 return false;
1092 }
1093
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001094 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001095 int bestT = -1;
1096 SkScalar top = bounds().fTop;
1097 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001098 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001099 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001100 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001101 end = nextSpan(start, 1);
1102 SkPoint edge[4];
1103 // OPTIMIZE: wrap this so that if start==0 end==fTCount-1 we can
1104 // work with the original data directly
1105 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001106 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001107 Intersections intersections;
1108 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1109 false, intersections);
1110 if (pts == 0) {
1111 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001112 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001113 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1114 // if the intersection is edge on, wait for another one
1115 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001116 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001117 SkASSERT(pts == 1); // FIXME: more code required to disambiguate
1118 SkPoint pt;
1119 double foundT = intersections.fT[0][0];
1120 (*SegmentXYAtT[fVerb])(fPts, foundT, &pt);
1121 if (bestY < pt.fY) {
1122 bestY = pt.fY;
1123 bestT = foundT < 1 ? start : end;
1124 hitT = foundT;
1125 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001126 } while (fTs[end].fT != 1);
1127 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001128 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001129
caryclark@google.com15fa1382012-05-07 20:49:36 +00001130 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001131 SkASSERT(fDoneSpans <= fTs.count());
1132 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001133 }
1134
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001135 // so the span needs to contain the pairing info found here
1136 // this should include the winding computed for the edge, and
1137 // what edge it connects to, and whether it is discarded
1138 // (maybe discarded == abs(winding) > 1) ?
1139 // only need derivatives for duration of sorting, add a new struct
1140 // for pairings, remove extra spans that have zero length and
1141 // reference an unused other
1142 // for coincident, the last span on the other may be marked done
1143 // (always?)
1144
1145 // if loop is exhausted, contour may be closed.
1146 // FIXME: pass in close point so we can check for closure
1147
1148 // given a segment, and a sense of where 'inside' is, return the next
1149 // segment. If this segment has an intersection, or ends in multiple
1150 // segments, find the mate that continues the outside.
1151 // note that if there are multiples, but no coincidence, we can limit
1152 // choices to connections in the correct direction
1153
1154 // mark found segments as done
1155
caryclark@google.com15fa1382012-05-07 20:49:36 +00001156 // start is the index of the beginning T of this edge
1157 // it is guaranteed to have an end which describes a non-zero length (?)
1158 // winding -1 means ccw, 1 means cw
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001159 // firstFind allows coincident edges to be treated differently
caryclark@google.com5c286d32012-07-13 11:57:28 +00001160 Segment* findNext(SkTDArray<Span*>& chase, int winding,
caryclark@google.com0e08a192012-07-13 21:07:52 +00001161 const int startIndex, const int endIndex, int& nextStart,
1162 int& nextEnd, int& flipped, bool firstFind, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001163 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001164 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001165 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1166 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001167 int step = SkSign32(endIndex - startIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001168 int end = nextSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001169 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001170 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001171 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001172 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001173 // mark the smaller of startIndex, endIndex done, and all adjacent
1174 // spans with the same T value (but not 'other' spans)
caryclark@google.com495f8e42012-05-31 13:13:11 +00001175 markDone(SkMin32(startIndex, endIndex), winding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001176 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001177 nextStart = endSpan->fOtherIndex;
1178 nextEnd = nextStart + step;
1179 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001180 return other;
1181 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001182 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001183 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001184 SkASSERT(startIndex - endIndex != 0);
1185 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001186 addTwoAngles(startIndex, end, angles);
1187 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001188 SkTDArray<Angle*> sorted;
1189 sortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001190 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001191 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001192 SkASSERT(firstIndex >= 0);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001193 #if DEBUG_WINDING
1194 SkDebugf("%s (first) winding=%d sign=%d\n", __FUNCTION__,
1195 winding, sorted[firstIndex]->sign());
1196 #endif
1197 bool innerSwap = false;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001198 int startWinding = winding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00001199 if (winding * sorted[firstIndex]->sign() > 0 && active) {
1200 // FIXME: this means winding was computed wrong by caller ?
1201 winding = 0;
1202 innerSwap = true;
1203 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001204 int nextIndex = firstIndex + 1;
1205 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1206 const Angle* foundAngle = NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001207 // iterate through the angle, and compute everyone's winding
1208 bool firstEdge = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001209 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001210 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001211 nextIndex = 0;
1212 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001213 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001214 int maxWinding = winding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001215 Segment* nextSegment = nextAngle->segment();
1216 int windValue = nextSegment->windValue(nextAngle);
1217 SkASSERT(windValue > 0);
1218 winding -= nextAngle->sign() * windValue;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001219 #if DEBUG_WINDING
caryclark@google.com0e08a192012-07-13 21:07:52 +00001220 SkDebugf("%s maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
1221 maxWinding, winding, nextAngle->sign());
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001222 #endif
1223 if (maxWinding * winding < 0) {
1224 flipped = -flipped;
1225 SkDebugf("flipped sign %d %d\n", maxWinding, winding);
1226 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001227 firstEdge = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001228 if (!winding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00001229 if (!active) {
1230 SkASSERT(nextAngle->segment() == this);
1231 markWinding(SkMin32(nextAngle->start(), nextAngle->end()),
1232 maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001233 #if DEBUG_WINDING
caryclark@google.com5c286d32012-07-13 11:57:28 +00001234 SkDebugf("%s inactive\n", __FUNCTION__);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001235 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00001236 return NULL;
1237 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001238 if (!foundAngle) {
1239 foundAngle = nextAngle;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001240 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001241 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001242 }
caryclark@google.com0e08a192012-07-13 21:07:52 +00001243 if (!maxWinding && innerSwap && !foundAngle) {
1244 foundAngle = nextAngle;
1245 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001246 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001247 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001248 }
1249 // if the winding is non-zero, nextAngle does not connect to
1250 // current chain. If we haven't done so already, mark the angle
1251 // as done, record the winding value, and mark connected unambiguous
1252 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001253 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001254 if (abs(maxWinding) < abs(winding)) {
1255 maxWinding = winding;
1256 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001257 Span* last;
caryclark@google.com0e08a192012-07-13 21:07:52 +00001258 if (foundAngle || innerSwap) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001259 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001260 } else {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001261 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
1262 }
1263 if (last) {
1264 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001265 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001266 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001267 } while (++nextIndex != lastIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001268 sorted[firstIndex]->segment()->
1269 markDone(SkMin32(startIndex, endIndex), startWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001270 if (!foundAngle) {
1271 return NULL;
1272 }
1273 nextStart = foundAngle->start();
1274 nextEnd = foundAngle->end();
1275 return foundAngle->segment();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001276 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001277
1278 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
1279 int angleCount = sorted.count();
1280 int firstIndex = -1;
1281 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
1282 const Angle* angle = sorted[angleIndex];
1283 if (angle->segment() == this && angle->start() == end &&
1284 angle->end() == start) {
1285 firstIndex = angleIndex;
1286 break;
1287 }
1288 }
1289 return firstIndex;
1290 }
1291
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001292 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001293 void findTooCloseToCall(int /* winding */ ) { // FIXME: winding should be considered
caryclark@google.com15fa1382012-05-07 20:49:36 +00001294 int count = fTs.count();
1295 if (count < 3) { // require t=0, x, 1 at minimum
1296 return;
1297 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001298 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001299 int moCount;
1300 Span* match;
1301 Segment* mOther;
1302 do {
1303 match = &fTs[matchIndex];
1304 mOther = match->fOther;
1305 moCount = mOther->fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001306 if (moCount >= 3) {
1307 break;
1308 }
1309 if (++matchIndex >= count) {
1310 return;
1311 }
1312 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00001313 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001314 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001315 // look for a pair of nearby T values that map to the same (x,y) value
1316 // if found, see if the pair of other segments share a common point. If
1317 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00001318 for (int index = matchIndex + 1; index < count; ++index) {
1319 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001320 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001321 continue;
1322 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001323 Segment* tOther = test->fOther;
1324 int toCount = tOther->fTs.count();
1325 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001326 continue;
1327 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001328 const SkPoint* testPt = &xyAtT(test);
1329 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001330 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001331 moCount = toCount;
1332 match = test;
1333 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001334 matchPt = testPt;
1335 continue;
1336 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001337 int moStart = -1;
1338 int moEnd = -1;
1339 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001340 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001341 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001342 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001343 continue;
1344 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001345 if (moSpan.fOther == this) {
1346 if (moSpan.fOtherT == match->fT) {
1347 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001348 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001349 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001350 continue;
1351 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001352 if (moSpan.fOther == tOther) {
1353 SkASSERT(moEnd == -1);
1354 moEnd = moIndex;
1355 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001356 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001357 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001358 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001359 continue;
1360 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001361 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
1362 if (moStartT == moEndT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001363 continue;
1364 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001365 int toStart = -1;
1366 int toEnd = -1;
1367 double toStartT, toEndT;
1368 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
1369 Span& toSpan = tOther->fTs[toIndex];
1370 if (toSpan.fOther == this) {
1371 if (toSpan.fOtherT == test->fT) {
1372 toStart = toIndex;
1373 toStartT = toSpan.fT;
1374 }
1375 continue;
1376 }
1377 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
1378 SkASSERT(toEnd == -1);
1379 toEnd = toIndex;
1380 toEndT = toSpan.fT;
1381 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001382 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001383 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
1384 if (toStart <= 0 || toEnd <= 0) {
1385 continue;
1386 }
1387 if (toStartT == toEndT) {
1388 continue;
1389 }
1390 // test to see if the segment between there and here is linear
1391 if (!mOther->isLinear(moStart, moEnd)
1392 || !tOther->isLinear(toStart, toEnd)) {
1393 continue;
1394 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001395 // FIXME: defer implementation until the rest works
1396 // this may share code with regular coincident detection
1397 SkASSERT(0);
1398 #if 0
1399 if (flipped) {
1400 mOther->addTCancel(moStart, moEnd, tOther, tStart, tEnd);
1401 } else {
1402 mOther->addTCoincident(moStart, moEnd, tOther, tStart, tEnd);
1403 }
1404 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001405 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001406 }
1407
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001408 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
1409 // and use more concise logic like the old edge walker code?
1410 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001411 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001412 // iterate through T intersections and return topmost
1413 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001414 SkASSERT(!done());
1415 int firstT;
1416 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001417 SkPoint topPt;
1418 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001419 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001420 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001421 bool lastDone = true;
1422 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001423 const Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001424 if (!span.fDone || !lastDone) {
1425 const SkPoint& intercept = xyAtT(&span);
1426 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
1427 && topPt.fX > intercept.fX)) {
1428 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001429 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001430 } else if (topPt == intercept) {
1431 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001432 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001433 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001434 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001435 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001436 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001437 int step = 1;
1438 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001439 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001440 step = -1;
1441 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001442 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001443 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001444 // if the topmost T is not on end, or is three-way or more, find left
1445 // look for left-ness from tLeft to firstT (matching y of other)
1446 SkTDArray<Angle> angles;
1447 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001448 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001449 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001450 SkTDArray<Angle*> sorted;
1451 sortAngles(angles, sorted);
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001452 // skip edges that have already been processed
1453 firstT = -1;
1454 Segment* leftSegment;
1455 do {
1456 const Angle* angle = sorted[++firstT];
1457 leftSegment = angle->segment();
1458 tIndex = angle->end();
1459 endIndex = angle->start();
1460 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001461 return leftSegment;
1462 }
1463
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001464 // FIXME: not crazy about this
1465 // when the intersections are performed, the other index is into an
1466 // incomplete array. as the array grows, the indices become incorrect
1467 // while the following fixes the indices up again, it isn't smart about
1468 // skipping segments whose indices are already correct
1469 // assuming we leave the code that wrote the index in the first place
1470 void fixOtherTIndex() {
1471 int iCount = fTs.count();
1472 for (int i = 0; i < iCount; ++i) {
1473 Span& iSpan = fTs[i];
1474 double oT = iSpan.fOtherT;
1475 Segment* other = iSpan.fOther;
1476 int oCount = other->fTs.count();
1477 for (int o = 0; o < oCount; ++o) {
1478 Span& oSpan = other->fTs[o];
1479 if (oT == oSpan.fT && this == oSpan.fOther) {
1480 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001481 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001482 }
1483 }
1484 }
1485 }
1486
caryclark@google.com495f8e42012-05-31 13:13:11 +00001487 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001488 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001489 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001490 SkASSERT(end >= 0);
1491 if (multipleSpans(end)) {
1492 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001493 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001494 const Span& endSpan = fTs[end];
1495 Segment* other = endSpan.fOther;
1496 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001497 int otherEnd = other->nextSpan(index, step);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001498 Span* last = other->innerChaseDone(index, step, winding);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001499 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001500 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001501 }
1502
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001503 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001504 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001505 SkASSERT(end >= 0);
1506 if (multipleSpans(end)) {
1507 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001508 }
1509 const Span& endSpan = fTs[end];
1510 Segment* other = endSpan.fOther;
1511 index = endSpan.fOtherIndex;
1512 int otherEnd = other->nextSpan(index, step);
1513 int min = SkMin32(index, otherEnd);
1514 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00001515 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001516 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001517 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001518 Span* last = other->innerChaseWinding(index, step, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001519 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001520 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001521 }
1522
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001523 void init(const SkPoint pts[], SkPath::Verb verb) {
1524 fPts = pts;
1525 fVerb = verb;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001526 fDoneSpans = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001527 }
1528
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001529 bool intersected() const {
1530 return fTs.count() > 0;
1531 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001532
1533 bool isLinear(int start, int end) const {
1534 if (fVerb == SkPath::kLine_Verb) {
1535 return true;
1536 }
1537 if (fVerb == SkPath::kQuad_Verb) {
1538 SkPoint qPart[3];
1539 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
1540 return QuadIsLinear(qPart);
1541 } else {
1542 SkASSERT(fVerb == SkPath::kCubic_Verb);
1543 SkPoint cPart[4];
1544 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
1545 return CubicIsLinear(cPart);
1546 }
1547 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001548
1549 // OPTIMIZE: successive calls could start were the last leaves off
1550 // or calls could specialize to walk forwards or backwards
1551 bool isMissing(double startT) const {
1552 size_t tCount = fTs.count();
1553 for (size_t index = 0; index < tCount; ++index) {
1554 if (fabs(startT - fTs[index].fT) < FLT_EPSILON) {
1555 return false;
1556 }
1557 }
1558 return true;
1559 }
1560
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001561 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001562 int count = fTs.count();
1563 if (count == 2) {
1564 return true;
1565 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001566 double t = fTs[end].fT;
1567 if (t < FLT_EPSILON) {
1568 return fTs[1].fT >= FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001569 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001570 if (t > 1 - FLT_EPSILON) {
1571 return fTs[count - 2].fT <= 1 - FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001572 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001573 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001574 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001575
1576 bool isHorizontal() const {
1577 return fBounds.fTop == fBounds.fBottom;
1578 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001579
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001580 bool isVertical() const {
1581 return fBounds.fLeft == fBounds.fRight;
1582 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001583
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001584 SkScalar leftMost(int start, int end) const {
1585 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
1586 }
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001587
caryclark@google.com495f8e42012-05-31 13:13:11 +00001588 // this span is excluded by the winding rule -- chase the ends
1589 // as long as they are unambiguous to mark connections as done
1590 // and give them the same winding value
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001591 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001592 int index = angle->start();
1593 int endIndex = angle->end();
1594 int step = SkSign32(endIndex - index);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001595 Span* last = innerChaseDone(index, step, winding);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001596 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001597 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001598 }
1599
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001600 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001601 int index = angle->start();
1602 int endIndex = angle->end();
1603 int min = SkMin32(index, endIndex);
1604 int step = SkSign32(endIndex - index);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001605 Span* last = innerChaseWinding(index, step, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001606 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001607 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001608 }
1609
caryclark@google.com495f8e42012-05-31 13:13:11 +00001610 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001611 // This may be called when the segment is already marked done. While this
1612 // wastes time, it shouldn't do any more than spin through the T spans.
1613 // OPTIMIZATION: abort on first done found (assuming that this code is
1614 // always called to mark segments done).
caryclark@google.com495f8e42012-05-31 13:13:11 +00001615 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001616 // SkASSERT(!done());
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001617 double referenceT = fTs[index].fT;
1618 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001619 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001620 Span& span = fTs[lesser];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001621 if (span.fDone) {
1622 continue;
1623 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001624 #if DEBUG_MARK_DONE
1625 const SkPoint& pt = xyAtT(&span);
1626 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1627 __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
1628 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001629 span.fDone = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001630 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1631 span.fWindSum = winding;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001632 fDoneSpans++;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001633 }
1634 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001635 Span& span = fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001636 // SkASSERT(!span.fDone);
1637 if (span.fDone) {
1638 continue;
1639 }
1640 #if DEBUG_MARK_DONE
1641 const SkPoint& pt = xyAtT(&span);
1642 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1643 __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
1644 #endif
1645 span.fDone = true;
1646 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1647 span.fWindSum = winding;
1648 fDoneSpans++;
1649 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
1650 }
1651
1652 void markWinding(int index, int winding) {
1653 SkASSERT(!done());
1654 double referenceT = fTs[index].fT;
1655 int lesser = index;
1656 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
1657 Span& span = fTs[lesser];
1658 if (span.fDone) {
1659 continue;
1660 }
1661 SkASSERT(span.fWindValue == 1 || winding == 0);
1662 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1663 #if DEBUG_MARK_DONE
1664 const SkPoint& pt = xyAtT(&span);
1665 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1666 __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
1667 #endif
1668 span.fWindSum = winding;
1669 }
1670 do {
1671 Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001672 // SkASSERT(!span.fDone || span.fCoincident);
1673 if (span.fDone) {
1674 continue;
1675 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001676 SkASSERT(span.fWindValue == 1 || winding == 0);
1677 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1678 #if DEBUG_MARK_DONE
1679 const SkPoint& pt = xyAtT(&span);
1680 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1681 __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
1682 #endif
1683 span.fWindSum = winding;
1684 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001685 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001686
caryclark@google.com9764cc62012-07-12 19:29:45 +00001687 // return span if when chasing, two or more radiating spans are not done
1688 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
1689 // candidate and the remaining spans have windValue == 0 (canceled by
1690 // coincidence). The coincident edges could either be removed altogether,
1691 // or this code could be more complicated in detecting this case. Worth it?
1692 bool multipleSpans(int end) const {
1693 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001694 }
1695
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001696 // This has callers for two different situations: one establishes the end
1697 // of the current span, and one establishes the beginning of the next span
1698 // (thus the name). When this is looking for the end of the current span,
1699 // coincidence is found when the beginning Ts contain -step and the end
1700 // contains step. When it is looking for the beginning of the next, the
1701 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001702 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001703 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001704 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001705 int count = fTs.count();
1706 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001707 while (step > 0 ? ++to < count : --to >= 0) {
1708 const Span& span = fTs[to];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001709 if ((step > 0 ? span.fT - fromSpan.fT : fromSpan.fT - span.fT) < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001710 continue;
1711 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001712 return to;
1713 }
1714 return -1;
1715 }
1716
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001717 const SkPoint* pts() const {
1718 return fPts;
1719 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001720
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001721 void reset() {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001722 init(NULL, (SkPath::Verb) -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001723 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1724 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001725 }
1726
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001727 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001728 const Span& span(int tIndex) const {
1729 return fTs[tIndex];
1730 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001731
1732 int spanSign(int startIndex, int endIndex) const {
1733 return startIndex < endIndex ? -fTs[startIndex].fWindValue :
1734 fTs[endIndex].fWindValue;
1735 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001736
1737 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001738 double t(int tIndex) const {
1739 return fTs[tIndex].fT;
1740 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001741
1742 void updatePts(const SkPoint pts[]) {
1743 fPts = pts;
1744 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001745
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001746 SkPath::Verb verb() const {
1747 return fVerb;
1748 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001749
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001750 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001751 return fTs[tIndex].fWindSum;
1752 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001753
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001754 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001755 int start = angle->start();
1756 int end = angle->end();
1757 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001758 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001759 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001760
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001761 int windValue(int tIndex) const {
1762 return fTs[tIndex].fWindValue;
1763 }
1764
1765 int windValue(const Angle* angle) const {
1766 int start = angle->start();
1767 int end = angle->end();
1768 int index = SkMin32(start, end);
1769 return windValue(index);
1770 }
1771
1772 SkScalar xAtT(const Span* span) const {
1773 return xyAtT(span).fX;
1774 }
1775
1776 const SkPoint& xyAtT(int index) const {
1777 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001778 }
1779
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001780 const SkPoint& xyAtT(const Span* span) const {
1781 if (!span->fPt) {
1782 if (span->fT == 0) {
1783 span->fPt = &fPts[0];
1784 } else if (span->fT == 1) {
1785 span->fPt = &fPts[fVerb];
1786 } else {
1787 SkPoint* pt = fIntersections.append();
1788 (*SegmentXYAtT[fVerb])(fPts, span->fT, pt);
1789 span->fPt = pt;
1790 }
1791 }
1792 return *span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001793 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001794
1795 SkScalar yAtT(int index) const {
1796 return yAtT(&fTs[index]);
1797 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001798
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001799 SkScalar yAtT(const Span* span) const {
1800 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001801 }
1802
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001803#if DEBUG_DUMP
1804 void dump() const {
1805 const char className[] = "Segment";
1806 const int tab = 4;
1807 for (int i = 0; i < fTs.count(); ++i) {
1808 SkPoint out;
1809 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
1810 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001811 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001812 tab + sizeof(className), className, fID,
1813 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001814 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001815 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001816 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001817 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00001818 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001819 }
1820#endif
1821
caryclark@google.com027de222012-07-12 12:52:50 +00001822#if DEBUG_ACTIVE_SPANS
1823 void debugShowActiveSpans(int contourID, int segmentIndex) {
1824 if (done()) {
1825 return;
1826 }
1827 for (int i = 0; i < fTs.count(); ++i) {
1828 if (fTs[i].fDone) {
1829 continue;
1830 }
1831 SkDebugf("%s contour=%d segment=%d (%d)", __FUNCTION__, contourID,
1832 segmentIndex, fID);
1833 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1834 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
1835 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1836 }
1837 const Span* span = &fTs[i];
1838 SkDebugf(") fT=%d (%1.9g) (%1.9g,%1.9g)", i, fTs[i].fT,
1839 xAtT(span), yAtT(i));
1840 const Segment* other = fTs[i].fOther;
1841 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d", other->fID,
1842 fTs[i].fOtherT, fTs[i].fOtherIndex);
1843 SkDebugf(" windSum=%d windValue=%d\n", fTs[i].fWindSum,
1844 fTs[i].fWindValue);
1845 }
1846 }
1847#endif
1848
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001849private:
1850 const SkPoint* fPts;
1851 SkPath::Verb fVerb;
1852 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001853 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001854 // OPTIMIZATION:if intersections array is a pointer, the it could only
1855 // be allocated as needed instead of always initialized -- though maybe
1856 // the initialization is lightweight enough that it hardly matters
1857 mutable SkTDArray<SkPoint> fIntersections;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001858 int fDoneSpans; // used for quick check that segment is finished
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001859#if DEBUG_DUMP
1860 int fID;
1861#endif
1862};
1863
caryclark@google.comb9738012012-07-03 19:53:30 +00001864class Contour;
1865
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001866struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00001867 Contour* fContours[2];
1868 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001869 double fTs[2][2];
1870};
1871
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001872class Contour {
1873public:
1874 Contour() {
1875 reset();
1876#if DEBUG_DUMP
1877 fID = ++gContourID;
1878#endif
1879 }
1880
1881 bool operator<(const Contour& rh) const {
1882 return fBounds.fTop == rh.fBounds.fTop
1883 ? fBounds.fLeft < rh.fBounds.fLeft
1884 : fBounds.fTop < rh.fBounds.fTop;
1885 }
1886
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001887 void addCoincident(int index, Contour* other, int otherIndex,
1888 const Intersections& ts, bool swap) {
1889 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00001890 coincidence.fContours[0] = this;
1891 coincidence.fContours[1] = other;
1892 coincidence.fSegments[0] = index;
1893 coincidence.fSegments[1] = otherIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001894 coincidence.fTs[swap][0] = ts.fT[0][0];
1895 coincidence.fTs[swap][1] = ts.fT[0][1];
1896 coincidence.fTs[!swap][0] = ts.fT[1][0];
1897 coincidence.fTs[!swap][1] = ts.fT[1][1];
1898 }
1899
1900 void addCross(const Contour* crosser) {
1901#ifdef DEBUG_CROSS
1902 for (int index = 0; index < fCrosses.count(); ++index) {
1903 SkASSERT(fCrosses[index] != crosser);
1904 }
1905#endif
1906 *fCrosses.append() = crosser;
1907 }
1908
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001909 void addCubic(const SkPoint pts[4]) {
1910 fSegments.push_back().addCubic(pts);
1911 fContainsCurves = true;
1912 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001913
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001914 int addLine(const SkPoint pts[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001915 fSegments.push_back().addLine(pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001916 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001917 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001918
1919 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
1920 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
1921 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001922
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001923 int addQuad(const SkPoint pts[3]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001924 fSegments.push_back().addQuad(pts);
1925 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001926 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001927 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001928
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001929 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
1930 containsIntercepts();
1931 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
1932 }
1933
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001934 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001935 return fBounds;
1936 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001937
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001938 void complete() {
1939 setBounds();
1940 fContainsIntercepts = false;
1941 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001942
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001943 void containsIntercepts() {
1944 fContainsIntercepts = true;
1945 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001946
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001947 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
1948 int &tIndex, double& hitT) {
1949 int segmentCount = fSegments.count();
1950 const Segment* bestSegment = NULL;
1951 for (int test = 0; test < segmentCount; ++test) {
1952 Segment* testSegment = &fSegments[test];
1953 const SkRect& bounds = testSegment->bounds();
1954 if (bounds.fTop < bestY) {
1955 continue;
1956 }
1957 if (bounds.fTop > basePt.fY) {
1958 continue;
1959 }
1960 if (bounds.fLeft > basePt.fX) {
1961 continue;
1962 }
1963 if (bounds.fRight < basePt.fX) {
1964 continue;
1965 }
1966 double testHitT;
1967 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
1968 if (testT >= 0) {
1969 bestSegment = testSegment;
1970 tIndex = testT;
1971 hitT = testHitT;
1972 }
1973 }
1974 return bestSegment;
1975 }
1976
1977 bool crosses(const Contour* crosser) const {
1978 if (this == crosser) {
1979 return true;
1980 }
1981 for (int index = 0; index < fCrosses.count(); ++index) {
1982 if (fCrosses[index] == crosser) {
1983 return true;
1984 }
1985 }
1986 return false;
1987 }
1988
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001989 void findTooCloseToCall(int winding) {
1990 int segmentCount = fSegments.count();
1991 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1992 fSegments[sIndex].findTooCloseToCall(winding);
1993 }
1994 }
1995
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001996 void fixOtherTIndex() {
1997 int segmentCount = fSegments.count();
1998 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1999 fSegments[sIndex].fixOtherTIndex();
2000 }
2001 }
2002
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002003 void reset() {
2004 fSegments.reset();
2005 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002006 fContainsCurves = fContainsIntercepts = false;
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002007 fWindingSum = SK_MinS32;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002008 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002009
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002010 void resolveCoincidence(int winding) {
2011 int count = fCoincidences.count();
2012 for (int index = 0; index < count; ++index) {
2013 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00002014 Contour* thisContour = coincidence.fContours[0];
2015 Contour* otherContour = coincidence.fContours[1];
2016 int thisIndex = coincidence.fSegments[0];
2017 int otherIndex = coincidence.fSegments[1];
2018 Segment& thisOne = thisContour->fSegments[thisIndex];
2019 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002020 double startT = coincidence.fTs[0][0];
2021 double endT = coincidence.fTs[0][1];
2022 if (startT > endT) {
2023 SkTSwap<double>(startT, endT);
2024 }
2025 SkASSERT(endT - startT >= FLT_EPSILON);
2026 double oStartT = coincidence.fTs[1][0];
2027 double oEndT = coincidence.fTs[1][1];
2028 if (oStartT > oEndT) {
2029 SkTSwap<double>(oStartT, oEndT);
2030 }
2031 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
caryclark@google.comb9738012012-07-03 19:53:30 +00002032 if (winding > 0 || thisOne.cancels(other)) {
2033 // make sure startT and endT have t entries
2034 if (thisOne.isMissing(startT) || other.isMissing(oEndT)) {
2035 thisOne.addTPair(startT, other, oEndT);
2036 }
2037 if (thisOne.isMissing(endT) || other.isMissing(oStartT)) {
2038 other.addTPair(oStartT, thisOne, endT);
2039 }
2040 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002041 } else {
caryclark@google.comb9738012012-07-03 19:53:30 +00002042 if (thisOne.isMissing(startT) || other.isMissing(oStartT)) {
2043 thisOne.addTPair(startT, other, oStartT);
2044 }
2045 if (thisOne.isMissing(endT) || other.isMissing(oEndT)) {
2046 other.addTPair(oEndT, thisOne, endT);
2047 }
2048 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002049 }
2050 }
2051 }
2052
2053 const SkTArray<Segment>& segments() {
2054 return fSegments;
2055 }
2056
2057 void setWinding(int winding) {
2058 SkASSERT(fWindingSum < 0);
2059 fWindingSum = winding;
2060 }
2061
caryclark@google.com15fa1382012-05-07 20:49:36 +00002062 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
2063 // we need to sort and walk edges in y, but that on the surface opens the
2064 // same can of worms as before. But then, this is a rough sort based on
2065 // segments' top, and not a true sort, so it could be ameniable to regular
2066 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002067 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002068 int segmentCount = fSegments.count();
2069 SkASSERT(segmentCount > 0);
2070 int best = -1;
2071 Segment* bestSegment = NULL;
2072 while (++best < segmentCount) {
2073 Segment* testSegment = &fSegments[best];
2074 if (testSegment->done()) {
2075 continue;
2076 }
2077 bestSegment = testSegment;
2078 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002079 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002080 if (!bestSegment) {
2081 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002082 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002083 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002084 for (int test = best + 1; test < segmentCount; ++test) {
2085 Segment* testSegment = &fSegments[test];
2086 if (testSegment->done()) {
2087 continue;
2088 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002089 if (testSegment->bounds().fTop > bestTop) {
2090 continue;
2091 }
2092 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002093 if (bestTop > testTop) {
2094 bestTop = testTop;
2095 bestSegment = testSegment;
2096 }
2097 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002098 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002099 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002100 }
2101
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002102 int updateSegment(int index, const SkPoint* pts) {
2103 Segment& segment = fSegments[index];
2104 segment.updatePts(pts);
2105 return segment.verb() + 1;
2106 }
2107
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002108 int windSum() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002109 if (fWindingSum >= 0) {
2110 return fWindingSum;
2111 }
2112 // check peers
2113 int count = fCrosses.count();
2114 for (int index = 0; index < count; ++index) {
2115 const Contour* crosser = fCrosses[index];
2116 if (0 <= crosser->fWindingSum) {
2117 fWindingSum = crosser->fWindingSum;
2118 break;
2119 }
2120 }
2121 return fWindingSum;
2122 }
2123
2124#if DEBUG_TEST
2125 SkTArray<Segment>& debugSegments() {
2126 return fSegments;
2127 }
2128#endif
2129
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002130#if DEBUG_DUMP
2131 void dump() {
2132 int i;
2133 const char className[] = "Contour";
2134 const int tab = 4;
2135 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
2136 for (i = 0; i < fSegments.count(); ++i) {
2137 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
2138 className, i);
2139 fSegments[i].dump();
2140 }
2141 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
2142 tab + sizeof(className), className,
2143 fBounds.fLeft, fBounds.fTop,
2144 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002145 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
2146 className, fContainsIntercepts);
2147 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
2148 className, fContainsCurves);
2149 }
2150#endif
2151
caryclark@google.com027de222012-07-12 12:52:50 +00002152#if DEBUG_ACTIVE_SPANS
2153 void debugShowActiveSpans() {
2154 for (int index = 0; index < fSegments.count(); ++index) {
2155 fSegments[index].debugShowActiveSpans(fID, index);
2156 }
2157 }
2158#endif
2159
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002160protected:
2161 void setBounds() {
2162 int count = fSegments.count();
2163 if (count == 0) {
2164 SkDebugf("%s empty contour\n", __FUNCTION__);
2165 SkASSERT(0);
2166 // FIXME: delete empty contour?
2167 return;
2168 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002169 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002170 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002171 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002172 }
2173 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002174
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002175private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002176 SkTArray<Segment> fSegments;
2177 SkTDArray<Coincidence> fCoincidences;
2178 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002179 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002180 bool fContainsIntercepts;
2181 bool fContainsCurves;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002182 int fWindingSum; // initial winding number outside
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002183#if DEBUG_DUMP
2184 int fID;
2185#endif
2186};
2187
2188class EdgeBuilder {
2189public:
2190
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002191EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002192 : fPath(path)
2193 , fCurrentContour(NULL)
2194 , fContours(contours)
2195{
2196#if DEBUG_DUMP
2197 gContourID = 0;
2198 gSegmentID = 0;
2199#endif
2200 walk();
2201}
2202
2203protected:
2204
2205void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002206 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002207 fCurrentContour->complete();
2208 fCurrentContour = NULL;
2209 }
2210}
2211
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002212void walk() {
2213 // FIXME:remove once we can access path pts directly
2214 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
2215 SkPoint pts[4];
2216 SkPath::Verb verb;
2217 do {
2218 verb = iter.next(pts);
2219 *fPathVerbs.append() = verb;
2220 if (verb == SkPath::kMove_Verb) {
2221 *fPathPts.append() = pts[0];
2222 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
2223 fPathPts.append(verb, &pts[1]);
2224 }
2225 } while (verb != SkPath::kDone_Verb);
2226 // FIXME: end of section to remove once path pts are accessed directly
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002227
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002228 SkPath::Verb reducedVerb;
2229 uint8_t* verbPtr = fPathVerbs.begin();
2230 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002231 const SkPoint* finalCurveStart = NULL;
2232 const SkPoint* finalCurveEnd = NULL;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002233 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
2234 switch (verb) {
2235 case SkPath::kMove_Verb:
2236 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002237 if (!fCurrentContour) {
2238 fCurrentContour = fContours.push_back_n(1);
2239 finalCurveEnd = pointsPtr++;
2240 *fExtra.append() = -1; // start new contour
2241 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002242 continue;
2243 case SkPath::kLine_Verb:
2244 // skip degenerate points
2245 if (pointsPtr[-1].fX != pointsPtr[0].fX
2246 || pointsPtr[-1].fY != pointsPtr[0].fY) {
2247 fCurrentContour->addLine(&pointsPtr[-1]);
2248 }
2249 break;
2250 case SkPath::kQuad_Verb:
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002251
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002252 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
2253 if (reducedVerb == 0) {
2254 break; // skip degenerate points
2255 }
2256 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002257 *fExtra.append() =
2258 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002259 break;
2260 }
2261 fCurrentContour->addQuad(&pointsPtr[-1]);
2262 break;
2263 case SkPath::kCubic_Verb:
2264 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
2265 if (reducedVerb == 0) {
2266 break; // skip degenerate points
2267 }
2268 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002269 *fExtra.append() =
2270 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002271 break;
2272 }
2273 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002274 *fExtra.append() =
2275 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002276 break;
2277 }
2278 fCurrentContour->addCubic(&pointsPtr[-1]);
2279 break;
2280 case SkPath::kClose_Verb:
2281 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002282 if (finalCurveStart && finalCurveEnd
2283 && *finalCurveStart != *finalCurveEnd) {
2284 *fReducePts.append() = *finalCurveStart;
2285 *fReducePts.append() = *finalCurveEnd;
2286 *fExtra.append() =
2287 fCurrentContour->addLine(fReducePts.end() - 2);
2288 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002289 complete();
2290 continue;
2291 default:
2292 SkDEBUGFAIL("bad verb");
2293 return;
2294 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002295 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002296 pointsPtr += verb;
2297 SkASSERT(fCurrentContour);
2298 }
2299 complete();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002300 if (fCurrentContour && !fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002301 fContours.pop_back();
2302 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002303 // correct pointers in contours since fReducePts may have moved as it grew
2304 int cIndex = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002305 int extraCount = fExtra.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002306 SkASSERT(extraCount == 0 || fExtra[0] == -1);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002307 int eIndex = 0;
2308 int rIndex = 0;
2309 while (++eIndex < extraCount) {
2310 int offset = fExtra[eIndex];
2311 if (offset < 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002312 ++cIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002313 continue;
2314 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002315 fCurrentContour = &fContours[cIndex];
2316 rIndex += fCurrentContour->updateSegment(offset - 1,
2317 &fReducePts[rIndex]);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002318 }
2319 fExtra.reset(); // we're done with this
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002320}
2321
2322private:
2323 const SkPath& fPath;
2324 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002325 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002326 Contour* fCurrentContour;
2327 SkTArray<Contour>& fContours;
2328 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002329 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002330};
2331
2332class Work {
2333public:
2334 enum SegmentType {
2335 kHorizontalLine_Segment = -1,
2336 kVerticalLine_Segment = 0,
2337 kLine_Segment = SkPath::kLine_Verb,
2338 kQuad_Segment = SkPath::kQuad_Verb,
2339 kCubic_Segment = SkPath::kCubic_Verb,
2340 };
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002341
2342 void addCoincident(Work& other, const Intersections& ts, bool swap) {
2343 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
2344 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002345
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002346 // FIXME: does it make sense to write otherIndex now if we're going to
2347 // fix it up later?
2348 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002349 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002350 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002351
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002352 // Avoid collapsing t values that are close to the same since
2353 // we walk ts to describe consecutive intersections. Since a pair of ts can
2354 // be nearly equal, any problems caused by this should be taken care
2355 // of later.
2356 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002357 int addT(double newT, const Work& other) {
2358 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002359 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002360
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002361 bool advance() {
2362 return ++fIndex < fLast;
2363 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002364
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002365 SkScalar bottom() const {
2366 return bounds().fBottom;
2367 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002368
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002369 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002370 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002371 }
2372
2373 const SkPoint* cubic() const {
2374 return fCubic;
2375 }
2376
2377 void init(Contour* contour) {
2378 fContour = contour;
2379 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002380 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002381 }
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002382
2383 bool isAdjacent(const Work& next) {
2384 return fContour == next.fContour && fIndex + 1 == next.fIndex;
2385 }
2386
2387 bool isFirstLast(const Work& next) {
2388 return fContour == next.fContour && fIndex == 0
2389 && next.fIndex == fLast - 1;
2390 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002391
2392 SkScalar left() const {
2393 return bounds().fLeft;
2394 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002395
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002396 void promoteToCubic() {
2397 fCubic[0] = pts()[0];
2398 fCubic[2] = pts()[1];
2399 fCubic[3] = pts()[2];
2400 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
2401 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
2402 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
2403 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
2404 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002405
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002406 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002407 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002408 }
2409
2410 SkScalar right() const {
2411 return bounds().fRight;
2412 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002413
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002414 ptrdiff_t segmentIndex() const {
2415 return fIndex;
2416 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002417
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002418 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002419 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002420 SegmentType type = (SegmentType) segment.verb();
2421 if (type != kLine_Segment) {
2422 return type;
2423 }
2424 if (segment.isHorizontal()) {
2425 return kHorizontalLine_Segment;
2426 }
2427 if (segment.isVertical()) {
2428 return kVerticalLine_Segment;
2429 }
2430 return kLine_Segment;
2431 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002432
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002433 bool startAfter(const Work& after) {
2434 fIndex = after.fIndex;
2435 return advance();
2436 }
2437
2438 SkScalar top() const {
2439 return bounds().fTop;
2440 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002441
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002442 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002443 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002444 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002445
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002446 SkScalar x() const {
2447 return bounds().fLeft;
2448 }
2449
2450 bool xFlipped() const {
2451 return x() != pts()[0].fX;
2452 }
2453
2454 SkScalar y() const {
2455 return bounds().fTop;
2456 }
2457
2458 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002459 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002460 }
2461
2462protected:
2463 Contour* fContour;
2464 SkPoint fCubic[4];
2465 int fIndex;
2466 int fLast;
2467};
2468
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002469#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002470static void debugShowLineIntersection(int pts, const Work& wt,
2471 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002472 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002473 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
2474 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
2475 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
2476 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002477 return;
2478 }
2479 SkPoint wtOutPt, wnOutPt;
2480 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
2481 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002482 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002483 __FUNCTION__,
2484 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
2485 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
2486 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002487 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002488 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002489 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002490 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
2491 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
2492 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002493 SkDebugf(" wnTs[1]=%g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002494 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002495 SkDebugf("\n");
2496}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002497#else
2498static void debugShowLineIntersection(int , const Work& ,
2499 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002500}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002501#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002502
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002503static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002504
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002505 if (test != next) {
2506 if (test->bounds().fBottom < next->bounds().fTop) {
2507 return false;
2508 }
2509 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
2510 return true;
2511 }
2512 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002513 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002514 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002515 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002516 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002517 Work wn;
2518 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002519 if (test == next && !wn.startAfter(wt)) {
2520 continue;
2521 }
2522 do {
2523 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
2524 continue;
2525 }
2526 int pts;
2527 Intersections ts;
2528 bool swap = false;
2529 switch (wt.segmentType()) {
2530 case Work::kHorizontalLine_Segment:
2531 swap = true;
2532 switch (wn.segmentType()) {
2533 case Work::kHorizontalLine_Segment:
2534 case Work::kVerticalLine_Segment:
2535 case Work::kLine_Segment: {
2536 pts = HLineIntersect(wn.pts(), wt.left(),
2537 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002538 debugShowLineIntersection(pts, wt, wn,
2539 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002540 break;
2541 }
2542 case Work::kQuad_Segment: {
2543 pts = HQuadIntersect(wn.pts(), wt.left(),
2544 wt.right(), wt.y(), wt.xFlipped(), ts);
2545 break;
2546 }
2547 case Work::kCubic_Segment: {
2548 pts = HCubicIntersect(wn.pts(), wt.left(),
2549 wt.right(), wt.y(), wt.xFlipped(), ts);
2550 break;
2551 }
2552 default:
2553 SkASSERT(0);
2554 }
2555 break;
2556 case Work::kVerticalLine_Segment:
2557 swap = true;
2558 switch (wn.segmentType()) {
2559 case Work::kHorizontalLine_Segment:
2560 case Work::kVerticalLine_Segment:
2561 case Work::kLine_Segment: {
2562 pts = VLineIntersect(wn.pts(), wt.top(),
2563 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002564 debugShowLineIntersection(pts, wt, wn,
2565 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002566 break;
2567 }
2568 case Work::kQuad_Segment: {
2569 pts = VQuadIntersect(wn.pts(), wt.top(),
2570 wt.bottom(), wt.x(), wt.yFlipped(), ts);
2571 break;
2572 }
2573 case Work::kCubic_Segment: {
2574 pts = VCubicIntersect(wn.pts(), wt.top(),
2575 wt.bottom(), wt.x(), wt.yFlipped(), ts);
2576 break;
2577 }
2578 default:
2579 SkASSERT(0);
2580 }
2581 break;
2582 case Work::kLine_Segment:
2583 switch (wn.segmentType()) {
2584 case Work::kHorizontalLine_Segment:
2585 pts = HLineIntersect(wt.pts(), wn.left(),
2586 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002587 debugShowLineIntersection(pts, wt, wn,
2588 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002589 break;
2590 case Work::kVerticalLine_Segment:
2591 pts = VLineIntersect(wt.pts(), wn.top(),
2592 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002593 debugShowLineIntersection(pts, wt, wn,
2594 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002595 break;
2596 case Work::kLine_Segment: {
2597 pts = LineIntersect(wt.pts(), wn.pts(), ts);
2598 debugShowLineIntersection(pts, wt, wn,
2599 ts.fT[1], ts.fT[0]);
2600 break;
2601 }
2602 case Work::kQuad_Segment: {
2603 swap = true;
2604 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
2605 break;
2606 }
2607 case Work::kCubic_Segment: {
2608 swap = true;
2609 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
2610 break;
2611 }
2612 default:
2613 SkASSERT(0);
2614 }
2615 break;
2616 case Work::kQuad_Segment:
2617 switch (wn.segmentType()) {
2618 case Work::kHorizontalLine_Segment:
2619 pts = HQuadIntersect(wt.pts(), wn.left(),
2620 wn.right(), wn.y(), wn.xFlipped(), ts);
2621 break;
2622 case Work::kVerticalLine_Segment:
2623 pts = VQuadIntersect(wt.pts(), wn.top(),
2624 wn.bottom(), wn.x(), wn.yFlipped(), ts);
2625 break;
2626 case Work::kLine_Segment: {
2627 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
2628 break;
2629 }
2630 case Work::kQuad_Segment: {
2631 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
2632 break;
2633 }
2634 case Work::kCubic_Segment: {
2635 wt.promoteToCubic();
2636 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
2637 break;
2638 }
2639 default:
2640 SkASSERT(0);
2641 }
2642 break;
2643 case Work::kCubic_Segment:
2644 switch (wn.segmentType()) {
2645 case Work::kHorizontalLine_Segment:
2646 pts = HCubicIntersect(wt.pts(), wn.left(),
2647 wn.right(), wn.y(), wn.xFlipped(), ts);
2648 break;
2649 case Work::kVerticalLine_Segment:
2650 pts = VCubicIntersect(wt.pts(), wn.top(),
2651 wn.bottom(), wn.x(), wn.yFlipped(), ts);
2652 break;
2653 case Work::kLine_Segment: {
2654 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
2655 break;
2656 }
2657 case Work::kQuad_Segment: {
2658 wn.promoteToCubic();
2659 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
2660 break;
2661 }
2662 case Work::kCubic_Segment: {
2663 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
2664 break;
2665 }
2666 default:
2667 SkASSERT(0);
2668 }
2669 break;
2670 default:
2671 SkASSERT(0);
2672 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002673 if (!foundCommonContour && pts > 0) {
2674 test->addCross(next);
2675 next->addCross(test);
2676 foundCommonContour = true;
2677 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002678 // in addition to recording T values, record matching segment
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002679 if (pts == 2 && wn.segmentType() <= Work::kLine_Segment
2680 && wt.segmentType() <= Work::kLine_Segment) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002681 wt.addCoincident(wn, ts, swap);
2682 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002683 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002684 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002685 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
2686 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002687 int testTAt = wt.addT(ts.fT[swap][pt], wn);
2688 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002689 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
2690 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002691 }
2692 } while (wn.advance());
2693 } while (wt.advance());
2694 return true;
2695}
2696
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002697// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002698// see if coincidence is formed by clipping non-concident segments
2699static void coincidenceCheck(SkTDArray<Contour*>& contourList, int winding) {
2700 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00002701 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002702 Contour* contour = contourList[cIndex];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002703 contour->findTooCloseToCall(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002704 }
2705 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
2706 Contour* contour = contourList[cIndex];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002707 contour->resolveCoincidence(winding);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002708 }
2709}
2710
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002711// project a ray from the top of the contour up and see if it hits anything
2712// note: when we compute line intersections, we keep track of whether
2713// two contours touch, so we need only look at contours not touching this one.
2714// OPTIMIZATION: sort contourList vertically to avoid linear walk
2715static int innerContourCheck(SkTDArray<Contour*>& contourList,
2716 Contour* baseContour, const SkPoint& basePt) {
2717 int contourCount = contourList.count();
2718 int winding = 0;
2719 SkScalar bestY = SK_ScalarMin;
2720 for (int cTest = 0; cTest < contourCount; ++cTest) {
2721 Contour* contour = contourList[cTest];
2722 if (basePt.fY < contour->bounds().fTop) {
2723 continue;
2724 }
2725 if (bestY > contour->bounds().fBottom) {
2726 continue;
2727 }
2728 if (baseContour->crosses(contour)) {
2729 continue;
2730 }
2731 int tIndex;
2732 double tHit;
2733 const Segment* test = contour->crossedSegment(basePt, bestY, tIndex,
2734 tHit);
2735 if (!test) {
2736 continue;
2737 }
2738 // If the ray hit the end of a span, we need to construct the wheel of
2739 // angles to find the span closest to the ray -- even if there are just
2740 // two spokes on the wheel.
2741 if (tHit == test->t(tIndex)) {
2742 SkTDArray<Angle> angles;
2743 int end = test->nextSpan(tIndex, 1);
2744 if (end < 0) {
2745 end = test->nextSpan(tIndex, -1);
2746 }
2747 test->addTwoAngles(tIndex, end, angles);
2748 // test->buildAnglesInner(tIndex, angles);
2749 test->buildAngles(tIndex, angles);
2750 SkTDArray<Angle*> sorted;
2751 sortAngles(angles, sorted);
2752 const Angle* angle = sorted[0];
2753 test = angle->segment();
2754 SkScalar testDx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
2755 if (testDx == 0) {
2756 angle = *(sorted.end() - 1);
2757 test = angle->segment();
2758 SkASSERT((*SegmentDXAtT[test->verb()])(test->pts(), tHit) != 0);
2759 }
2760 tIndex = angle->start(); // lesser Y
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002761 winding = test->windSum(SkMin32(tIndex, angle->end()));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002762 #if DEBUG_WINDING
2763 SkDebugf("%s 1 winding=%d\n", __FUNCTION__, winding);
2764 #endif
2765 } else {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002766 winding = test->windSum(tIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002767 #if DEBUG_WINDING
2768 SkDebugf("%s 2 winding=%d\n", __FUNCTION__, winding);
2769 #endif
2770 }
2771 // see if a + change in T results in a +/- change in X (compute x'(T))
2772 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
2773 #if DEBUG_WINDING
2774 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
2775 #endif
2776 SkASSERT(dx != 0);
2777 if (winding * dx > 0) { // if same signs, result is negative
2778 winding += dx > 0 ? -1 : 1;
2779 #if DEBUG_WINDING
2780 SkDebugf("%s 3 winding=%d\n", __FUNCTION__, winding);
2781 #endif
2782 }
2783 }
2784 baseContour->setWinding(winding);
2785 return winding;
2786}
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002787
2788// OPTIMIZATION: not crazy about linear search here to find top active y.
2789// seems like we should break down and do the sort, or maybe sort each
2790// contours' segments?
2791// Once the segment array is built, there's no reason I can think of not to
2792// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002793// FIXME: return the contour found to pass to inner contour check
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002794static Segment* findTopContour(SkTDArray<Contour*>& contourList,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002795 Contour*& topContour) {
2796 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002797 int cIndex = 0;
2798 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002799 SkScalar bestY = SK_ScalarMax;
2800 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002801 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002802 contour = contourList[cIndex];
2803 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002804 } while (!topStart && ++cIndex < contourCount);
2805 if (!topStart) {
2806 return NULL;
2807 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002808 topContour = contour;
2809 while (++cIndex < contourCount) {
2810 contour = contourList[cIndex];
2811 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002812 continue;
2813 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002814 SkScalar testY = SK_ScalarMax;
2815 Segment* test = contour->topSegment(testY);
2816 if (!test || bestY <= testY) {
2817 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002818 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002819 topContour = contour;
2820 topStart = test;
2821 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002822 }
2823 return topStart;
2824}
2825
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002826static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
2827 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00002828 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002829 const Span& backPtr = span->fOther->span(span->fOtherIndex);
2830 Segment* segment = backPtr.fOther;
2831 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00002832 SkTDArray<Angle> angles;
2833 int done = 0;
2834 if (segment->activeAngle(tIndex, done, angles)) {
2835 Angle* last = angles.end() - 1;
2836 tIndex = last->start();
2837 endIndex = last->end();
2838 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002839 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00002840 if (done == angles.count()) {
2841 chase.pop(&span);
2842 continue;
2843 }
2844 SkTDArray<Angle*> sorted;
2845 sortAngles(angles, sorted);
2846 // find first angle, initialize winding to computed fWindSum
2847 int firstIndex = -1;
2848 const Angle* angle;
2849 int winding;
2850 do {
2851 angle = sorted[++firstIndex];
2852 winding = angle->segment()->windSum(angle);
2853 } while (winding == SK_MinS32);
2854 int firstSign = angle->sign();
2855 if (firstSign * winding > 0) {
2856 winding -= firstSign;
2857 }
caryclark@google.com210acaf2012-07-12 21:05:13 +00002858 // SkDebugf("%s firstSign=%d\n", __FUNCTION__, firstSign);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002859 // we care about first sign and whether wind sum indicates this
2860 // edge is inside or outside. Maybe need to pass span winding
2861 // or first winding or something into this function?
2862 // advance to first undone angle, then return it and winding
2863 // (to set whether edges are active or not)
2864 int nextIndex = firstIndex + 1;
2865 int angleCount = sorted.count();
2866 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2867 do {
2868 SkASSERT(nextIndex != firstIndex);
2869 if (nextIndex == angleCount) {
2870 nextIndex = 0;
2871 }
2872 const Angle* angle = sorted[nextIndex];
2873 segment = angle->segment();
2874 int windValue = segment->windValue(angle);
2875 SkASSERT(windValue > 0);
2876 int maxWinding = winding;
2877 winding -= angle->sign() * windValue;
2878 if (maxWinding * winding < 0) {
2879 SkDebugf("%s flipped sign %d %d\n", __FUNCTION__, maxWinding, winding);
2880 }
2881 tIndex = angle->start();
2882 endIndex = angle->end();
2883 int lesser = SkMin32(tIndex, endIndex);
2884 const Span& nextSpan = segment->span(lesser);
2885 if (!nextSpan.fDone) {
2886 // FIXME: this be wrong. assign startWinding if edge is in
2887 // same direction. If the direction is opposite, winding to
2888 // assign is flipped sign or +/- 1?
2889 if (abs(maxWinding) < abs(winding)) {
2890 maxWinding = winding;
2891 }
2892 segment->markWinding(lesser, maxWinding);
2893 break;
2894 }
2895 } while (++nextIndex != lastIndex);
2896 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002897 }
2898 return NULL;
2899}
2900
caryclark@google.com027de222012-07-12 12:52:50 +00002901#if DEBUG_ACTIVE_SPANS
2902static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
2903 for (int index = 0; index < contourList.count(); ++ index) {
2904 contourList[index]->debugShowActiveSpans();
2905 }
2906}
2907#endif
2908
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002909// Each segment may have an inside or an outside. Segments contained within
2910// winding may have insides on either side, and form a contour that should be
2911// ignored. Segments that are coincident with opposing direction segments may
2912// have outsides on either side, and should also disappear.
2913// 'Normal' segments will have one inside and one outside. Subsequent connections
2914// when winding should follow the intersection direction. If more than one edge
2915// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002916 // since we start with leftmost top edge, we'll traverse through a
2917 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002918static void bridge(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002919 bool firstContour = true;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002920 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002921 Contour* topContour;
2922 Segment* topStart = findTopContour(contourList, topContour);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002923 if (!topStart) {
2924 break;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002925 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002926 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00002927 // follow edges to intersection by changing the index by direction.
2928 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002929 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002930 int contourWinding;
2931 if (firstContour) {
2932 contourWinding = 0;
2933 firstContour = false;
2934 } else {
2935 const SkPoint& topPoint = current->xyAtT(endIndex);
2936 contourWinding = innerContourCheck(contourList, topContour, topPoint);
2937#if DEBUG_WINDING
2938 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
2939#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002940 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002941 SkPoint lastPt;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002942 bool firstTime = true;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002943 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002944 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002945 // int firstWinding = contourWinding + spanWinding;
2946 // FIXME: needs work. While it works in limited situations, it does
2947 // not always compute winding correctly. Active should be removed and instead
2948 // the initial winding should be correctly passed in so that if the
2949 // inner contour is wound the same way, it never finds an accumulated
2950 // winding of zero. Inside 'find next', we need to look for transitions
2951 // other than zero when resolving sorted angles.
2952 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002953 do {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002954 bool active = winding * spanWinding <= 0;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002955 #if DEBUG_WINDING
2956 if (!active) {
2957 SkDebugf("%s !active winding=%d spanWinding=%d\n",
2958 __FUNCTION__, winding, spanWinding);
2959 }
2960 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002961 const SkPoint* firstPt = NULL;
2962 do {
2963 SkASSERT(!current->done());
2964 int nextStart, nextEnd, flipped = 1;
2965 Segment* next = current->findNext(chaseArray,
caryclark@google.com0e08a192012-07-13 21:07:52 +00002966 winding + spanWinding, index, endIndex,
2967 nextStart, nextEnd, flipped, firstTime, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002968 if (!next) {
2969 break;
2970 }
2971 if (!firstPt) {
2972 firstPt = &current->addMoveTo(index, simple, active);
2973 }
2974 lastPt = current->addCurveTo(index, endIndex, simple, active);
2975 current = next;
2976 index = nextStart;
2977 endIndex = nextEnd;
2978 spanWinding = SkSign32(spanWinding) * flipped * next->windValue(
2979 SkMin32(nextStart, nextEnd));
2980 #if DEBUG_WINDING
2981 SkDebugf("%s spanWinding=%d\n", __FUNCTION__, spanWinding);
2982 #endif
2983 firstTime = false;
2984 } while (*firstPt != lastPt && (active || !current->done()));
2985 if (firstPt && active) {
2986 #if DEBUG_PATH_CONSTRUCTION
2987 SkDebugf("%s close\n", __FUNCTION__);
2988 #endif
2989 simple.close();
2990 }
2991 current = findChase(chaseArray, index, endIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002992 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00002993 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002994 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002995 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002996 break;
2997 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002998 int lesser = SkMin32(index, endIndex);
2999 spanWinding = current->windSum(lesser);
3000 int spanValue = current->windValue(lesser);
3001 SkASSERT(spanWinding != SK_MinS32);
3002 int spanSign = current->spanSign(index, endIndex);
3003 #if DEBUG_WINDING
3004 SkDebugf("%s spanWinding=%d spanSign=%d winding=%d spanValue=%d\n",
3005 __FUNCTION__, spanWinding, spanSign, winding, spanValue);
3006 #endif
3007 if (spanWinding * spanSign < 0) {
3008 #if DEBUG_WINDING
3009 SkDebugf("%s spanWinding * spanSign < 0\n", __FUNCTION__);
3010 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003011 // SkTSwap<int>(index, endIndex);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003012 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003013 if (abs(spanWinding) > spanValue) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003014 winding = spanWinding;
3015 spanWinding = spanValue * SkSign32(spanWinding);
3016 winding -= spanWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00003017 #if DEBUG_WINDING
3018 SkDebugf("%s spanWinding=%d winding=%d\n", __FUNCTION__,
3019 spanWinding, winding);
3020 #endif
3021 } else {
3022 #if DEBUG_WINDING
3023 SkDebugf("%s ->0 contourWinding=%d winding=%d\n", __FUNCTION__,
3024 contourWinding, winding);
3025 #endif
3026 winding = 0;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003027 }
3028 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003029 } while (true);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003030}
3031
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003032static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
3033 int contourCount = contourList.count();
3034 for (int cTest = 0; cTest < contourCount; ++cTest) {
3035 Contour* contour = contourList[cTest];
3036 contour->fixOtherTIndex();
3037 }
3038}
3039
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003040static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003041 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003042 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003043 if (count == 0) {
3044 return;
3045 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003046 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003047 *list.append() = &contours[index];
3048 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003049 QSort<Contour>(list.begin(), list.end() - 1);
3050}
3051
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003052void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003053 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003054 int winding = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003055 simple.reset();
3056 simple.setFillType(SkPath::kEvenOdd_FillType);
3057
3058 // turn path into list of segments
3059 SkTArray<Contour> contours;
3060 // FIXME: add self-intersecting cubics' T values to segment
3061 EdgeBuilder builder(path, contours);
3062 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003063 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003064 Contour** currentPtr = contourList.begin();
3065 if (!currentPtr) {
3066 return;
3067 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003068 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003069 // find all intersections between segments
3070 do {
3071 Contour** nextPtr = currentPtr;
3072 Contour* current = *currentPtr++;
3073 Contour* next;
3074 do {
3075 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003076 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003077 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003078 // eat through coincident edges
3079 coincidenceCheck(contourList, winding);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003080 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003081 // construct closed contours
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003082 bridge(contourList, simple);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003083}
3084