blob: 7f1e7c7866e2d4bc1800c04f4a3059a6c79b2e18 [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
caryclark@google.com47580692012-07-23 12:14:49 +000020// An Edge is a Segment generated from a Span
caryclark@google.com15fa1382012-05-07 20:49:36 +000021
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022// FIXME: remove once debugging is complete
caryclark@google.com47580692012-07-23 12:14:49 +000023#ifdef SK_DEBUG
24int gDebugMaxWindSum = SK_MaxS32;
25int gDebugMaxWindValue = SK_MaxS32;
26#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +000027
caryclark@google.com47580692012-07-23 12:14:49 +000028#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa0588f2012-04-26 21:01:06 +000029
caryclark@google.com47580692012-07-23 12:14:49 +000030#if 0 // set to 1 for multiple thread -- no debugging
31
32const bool gRunTestsInOneThread = false;
33
34#define DEBUG_ACTIVE_SPANS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000035#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000036#define DEBUG_ADD_T_PAIR 0
caryclark@google.com47580692012-07-23 12:14:49 +000037#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000038#define DEBUG_CROSS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000039#define DEBUG_DUMP 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000040#define DEBUG_MARK_DONE 0
caryclark@google.com47580692012-07-23 12:14:49 +000041#define DEBUG_PATH_CONSTRUCTION 0
42#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000043#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000044#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000045
46#else
47
caryclark@google.com47580692012-07-23 12:14:49 +000048const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000049
caryclark@google.comafe56de2012-07-24 18:11:03 +000050#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000051#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.come21cb182012-07-23 21:26:31 +000052#define DEBUG_ADD_T_PAIR 0
caryclark@google.com18063442012-07-25 12:05:18 +000053#define DEBUG_CONCIDENT 01
caryclark@google.come21cb182012-07-23 21:26:31 +000054#define DEBUG_CROSS 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000055#define DEBUG_DUMP 1
caryclark@google.com47580692012-07-23 12:14:49 +000056#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000057#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000058#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000059#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000060#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000061
62#endif
63
caryclark@google.com47580692012-07-23 12:14:49 +000064#if (DEBUG_ACTIVE_SPANS || DEBUG_CONCIDENT) && !DEBUG_DUMP
caryclark@google.com027de222012-07-12 12:52:50 +000065#undef DEBUG_DUMP
66#define DEBUG_DUMP 1
67#endif
68
caryclark@google.comfa0588f2012-04-26 21:01:06 +000069#if DEBUG_DUMP
70static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000071// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000072static int gContourID;
73static int gSegmentID;
74#endif
75
caryclark@google.com8dcf1142012-07-02 20:27:02 +000076#ifndef DEBUG_TEST
77#define DEBUG_TEST 0
78#endif
79
caryclark@google.comfa0588f2012-04-26 21:01:06 +000080static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
81 Intersections& intersections) {
82 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
83 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
84 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
85}
86
87static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
88 Intersections& intersections) {
89 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
90 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
91 intersect(aQuad, bLine, intersections);
92 return intersections.fUsed;
93}
94
95static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
96 Intersections& intersections) {
97 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
98 {a[3].fX, a[3].fY}};
99 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
100 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
101}
102
103static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
104 Intersections& intersections) {
105 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
106 const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
107 intersect(aQuad, bQuad, intersections);
108 return intersections.fUsed;
109}
110
111static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
112 Intersections& intersections) {
113 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
114 {a[3].fX, a[3].fY}};
115 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
116 {b[3].fX, b[3].fY}};
117 intersect(aCubic, bCubic, intersections);
118 return intersections.fUsed;
119}
120
121static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
122 SkScalar y, bool flipped, Intersections& intersections) {
123 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
124 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
125}
126
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000127static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
128 SkScalar y, bool flipped, Intersections& intersections) {
129 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
130 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
131}
132
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000133static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
134 SkScalar y, bool flipped, Intersections& intersections) {
135 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
136 {a[3].fX, a[3].fY}};
137 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
138}
139
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000140static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
141 SkScalar x, bool flipped, Intersections& intersections) {
142 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
143 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
144}
145
146static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
147 SkScalar x, bool flipped, Intersections& intersections) {
148 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
149 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
150}
151
152static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
153 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000154 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
155 {a[3].fX, a[3].fY}};
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000156 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000157}
158
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000159static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
160 SkScalar , SkScalar , bool , Intersections& ) = {
161 NULL,
162 VLineIntersect,
163 VQuadIntersect,
164 VCubicIntersect
165};
166
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000167static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
168 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
169 double x, y;
170 xy_at_t(line, t, x, y);
171 out->fX = SkDoubleToScalar(x);
172 out->fY = SkDoubleToScalar(y);
173}
174
175static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
176 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
177 double x, y;
178 xy_at_t(quad, t, x, y);
179 out->fX = SkDoubleToScalar(x);
180 out->fY = SkDoubleToScalar(y);
181}
182
183static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
184 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
185 {a[3].fX, a[3].fY}};
186 double x, y;
187 xy_at_t(cubic, t, x, y);
188 out->fX = SkDoubleToScalar(x);
189 out->fY = SkDoubleToScalar(y);
190}
191
192static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
193 NULL,
194 LineXYAtT,
195 QuadXYAtT,
196 CubicXYAtT
197};
198
199static SkScalar LineXAtT(const SkPoint a[2], double t) {
200 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
201 double x;
202 xy_at_t(aLine, t, x, *(double*) 0);
203 return SkDoubleToScalar(x);
204}
205
206static SkScalar QuadXAtT(const SkPoint a[3], double t) {
207 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
208 double x;
209 xy_at_t(quad, t, x, *(double*) 0);
210 return SkDoubleToScalar(x);
211}
212
213static SkScalar CubicXAtT(const SkPoint a[4], double t) {
214 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
215 {a[3].fX, a[3].fY}};
216 double x;
217 xy_at_t(cubic, t, x, *(double*) 0);
218 return SkDoubleToScalar(x);
219}
220
221static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
222 NULL,
223 LineXAtT,
224 QuadXAtT,
225 CubicXAtT
226};
227
228static SkScalar LineYAtT(const SkPoint a[2], double t) {
229 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
230 double y;
231 xy_at_t(aLine, t, *(double*) 0, y);
232 return SkDoubleToScalar(y);
233}
234
235static SkScalar QuadYAtT(const SkPoint a[3], double t) {
236 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
237 double y;
238 xy_at_t(quad, t, *(double*) 0, y);
239 return SkDoubleToScalar(y);
240}
241
242static SkScalar CubicYAtT(const SkPoint a[4], double t) {
243 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
244 {a[3].fX, a[3].fY}};
245 double y;
246 xy_at_t(cubic, t, *(double*) 0, y);
247 return SkDoubleToScalar(y);
248}
249
250static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
251 NULL,
252 LineYAtT,
253 QuadYAtT,
254 CubicYAtT
255};
256
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000257static SkScalar LineDXAtT(const SkPoint a[2], double ) {
258 return a[1].fX - a[0].fX;
259}
260
261static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
262 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
263 double x;
264 dxdy_at_t(quad, t, x, *(double*) 0);
265 return SkDoubleToScalar(x);
266}
267
268static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
269 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
270 {a[3].fX, a[3].fY}};
271 double x;
272 dxdy_at_t(cubic, t, x, *(double*) 0);
273 return SkDoubleToScalar(x);
274}
275
276static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
277 NULL,
278 LineDXAtT,
279 QuadDXAtT,
280 CubicDXAtT
281};
282
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000283static void LineSubDivide(const SkPoint a[2], double startT, double endT,
284 SkPoint sub[2]) {
285 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
286 _Line dst;
287 sub_divide(aLine, 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}
293
294static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
295 SkPoint sub[3]) {
296 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
297 {a[2].fX, a[2].fY}};
298 Quadratic dst;
299 sub_divide(aQuad, startT, endT, dst);
300 sub[0].fX = SkDoubleToScalar(dst[0].x);
301 sub[0].fY = SkDoubleToScalar(dst[0].y);
302 sub[1].fX = SkDoubleToScalar(dst[1].x);
303 sub[1].fY = SkDoubleToScalar(dst[1].y);
304 sub[2].fX = SkDoubleToScalar(dst[2].x);
305 sub[2].fY = SkDoubleToScalar(dst[2].y);
306}
307
308static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
309 SkPoint sub[4]) {
310 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
311 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
312 Cubic dst;
313 sub_divide(aCubic, startT, endT, dst);
314 sub[0].fX = SkDoubleToScalar(dst[0].x);
315 sub[0].fY = SkDoubleToScalar(dst[0].y);
316 sub[1].fX = SkDoubleToScalar(dst[1].x);
317 sub[1].fY = SkDoubleToScalar(dst[1].y);
318 sub[2].fX = SkDoubleToScalar(dst[2].x);
319 sub[2].fY = SkDoubleToScalar(dst[2].y);
320 sub[3].fX = SkDoubleToScalar(dst[3].x);
321 sub[3].fY = SkDoubleToScalar(dst[3].y);
322}
323
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000324static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
325 SkPoint []) = {
326 NULL,
327 LineSubDivide,
328 QuadSubDivide,
329 CubicSubDivide
330};
331
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000332#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000333static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
334 SkRect& bounds) {
335 SkPoint dst[3];
336 QuadSubDivide(a, startT, endT, dst);
337 bounds.fLeft = bounds.fRight = dst[0].fX;
338 bounds.fTop = bounds.fBottom = dst[0].fY;
339 for (int index = 1; index < 3; ++index) {
340 bounds.growToInclude(dst[index].fX, dst[index].fY);
341 }
342}
343
344static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
345 SkRect& bounds) {
346 SkPoint dst[4];
347 CubicSubDivide(a, startT, endT, dst);
348 bounds.fLeft = bounds.fRight = dst[0].fX;
349 bounds.fTop = bounds.fBottom = dst[0].fY;
350 for (int index = 1; index < 4; ++index) {
351 bounds.growToInclude(dst[index].fX, dst[index].fY);
352 }
353}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000354#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000355
caryclark@google.com15fa1382012-05-07 20:49:36 +0000356static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000357 SkTDArray<SkPoint>& reducePts) {
358 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
359 {a[2].fX, a[2].fY}};
360 Quadratic dst;
361 int order = reduceOrder(aQuad, dst);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000362 if (order == 3) {
363 return SkPath::kQuad_Verb;
364 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000365 for (int index = 0; index < order; ++index) {
366 SkPoint* pt = reducePts.append();
367 pt->fX = SkDoubleToScalar(dst[index].x);
368 pt->fY = SkDoubleToScalar(dst[index].y);
369 }
370 return (SkPath::Verb) (order - 1);
371}
372
373static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
374 SkTDArray<SkPoint>& reducePts) {
375 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
376 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
377 Cubic dst;
378 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000379 if (order == 4) {
380 return SkPath::kCubic_Verb;
381 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000382 for (int index = 0; index < order; ++index) {
383 SkPoint* pt = reducePts.append();
384 pt->fX = SkDoubleToScalar(dst[index].x);
385 pt->fY = SkDoubleToScalar(dst[index].y);
386 }
387 return (SkPath::Verb) (order - 1);
388}
389
caryclark@google.com15fa1382012-05-07 20:49:36 +0000390static bool QuadIsLinear(const SkPoint a[3]) {
391 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
392 {a[2].fX, a[2].fY}};
393 return isLinear(aQuad, 0, 2);
394}
395
396static bool CubicIsLinear(const SkPoint a[4]) {
397 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
398 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
399 return isLinear(aCubic, 0, 3);
400}
401
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000402static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
403 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
404 double x[2];
405 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000406 xy_at_t(aLine, endT, x[1], *(double*) 0);
407 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000408}
409
410static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
411 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
412 {a[2].fX, a[2].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000413 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000414}
415
416static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
417 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
418 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000419 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000420}
421
422static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
423 NULL,
424 LineLeftMost,
425 QuadLeftMost,
426 CubicLeftMost
427};
428
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000429#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000430static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
431 const SkPoint& below) {
432 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
433 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
434 return implicit_matches_ulps(aLine, bLine, 32);
435}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000436#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000437
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000438class Segment;
439
caryclark@google.com15fa1382012-05-07 20:49:36 +0000440// sorting angles
441// given angles of {dx dy ddx ddy dddx dddy} sort them
442class Angle {
443public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000444 // FIXME: this is bogus for quads and cubics
445 // if the quads and cubics' line from end pt to ctrl pt are coincident,
446 // there's no obvious way to determine the curve ordering from the
447 // derivatives alone. In particular, if one quadratic's coincident tangent
448 // is longer than the other curve, the final control point can place the
449 // longer curve on either side of the shorter one.
450 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
451 // may provide some help, but nothing has been figured out yet.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000452 bool operator<(const Angle& rh) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000453 if ((fDy < 0) ^ (rh.fDy < 0)) {
454 return fDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000455 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000456 if (fDy == 0 && rh.fDy == 0 && fDx != rh.fDx) {
457 return fDx < rh.fDx;
458 }
459 SkScalar cmp = fDx * rh.fDy - rh.fDx * fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000460 if (cmp) {
461 return cmp < 0;
462 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000463 if ((fDDy < 0) ^ (rh.fDDy < 0)) {
464 return fDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000465 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000466 if (fDDy == 0 && rh.fDDy == 0 && fDDx != rh.fDDx) {
467 return fDDx < rh.fDDx;
468 }
469 cmp = fDDx * rh.fDDy - rh.fDDx * fDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000470 if (cmp) {
471 return cmp < 0;
472 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000473 if ((fDDDy < 0) ^ (rh.fDDDy < 0)) {
474 return fDDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000475 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000476 if (fDDDy == 0 && rh.fDDDy == 0) {
477 return fDDDx < rh.fDDDx;
478 }
479 return fDDDx * rh.fDDDy < rh.fDDDx * fDDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000480 }
caryclark@google.com47580692012-07-23 12:14:49 +0000481
482 double dx() const {
483 return fDx;
484 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000485
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000486 int end() const {
487 return fEnd;
488 }
489
caryclark@google.comcc905052012-07-25 20:59:42 +0000490 bool firstBump(int sumWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +0000491 return sign() * sumWinding > 0;
492 }
493
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000494 bool isHorizontal() const {
495 return fDy == 0 && fDDy == 0 && fDDDy == 0;
496 }
497
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000498 // since all angles share a point, this needs to know which point
499 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
500 // practically, this should only be called by addAngle
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000501 void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000502 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000503 SkASSERT(start != end);
504 fSegment = segment;
505 fStart = start;
506 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000507 fDx = pts[1].fX - pts[0].fX; // b - a
508 fDy = pts[1].fY - pts[0].fY;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000509 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000510 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000511 return;
512 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000513 fDDx = pts[2].fX - pts[1].fX - fDx; // a - 2b + c
514 fDDy = pts[2].fY - pts[1].fY - fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000515 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000516 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000517 return;
518 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000519 fDDDx = pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX;
520 fDDDy = pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY;
521 }
522
523 // noncoincident quads/cubics may have the same initial angle
524 // as lines, so must sort by derivatives as well
525 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000526 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000527 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000528 fSegment = segment;
529 fStart = start;
530 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000531 fDx = pts[1].fX - pts[0].fX; // b - a
532 fDy = pts[1].fY - pts[0].fY;
533 if (verb == SkPath::kLine_Verb) {
534 fDDx = fDDy = fDDDx = fDDDy = 0;
535 return;
536 }
537 if (verb == SkPath::kQuad_Verb) {
538 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
539 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
540 int larger = std::max(abs(uplsX), abs(uplsY));
541 int shift = 0;
542 double flatT;
543 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
544 LineParameters implicitLine;
545 _Line tangent = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}};
546 implicitLine.lineEndPoints(tangent);
547 implicitLine.normalize();
548 while (larger > UlpsEpsilon * 1024) {
549 larger >>= 2;
550 ++shift;
551 flatT = 0.5 / (1 << shift);
552 QuadXYAtT(pts, flatT, &ddPt);
553 _Point _pt = {ddPt.fX, ddPt.fY};
554 double distance = implicitLine.pointDistance(_pt);
555 if (approximately_zero(distance)) {
556 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
557 break;
558 }
559 }
560 flatT = 0.5 / (1 << shift);
561 QuadXYAtT(pts, flatT, &ddPt);
562 fDDx = ddPt.fX - pts[0].fX;
563 fDDy = ddPt.fY - pts[0].fY;
564 SkASSERT(fDDx != 0 || fDDy != 0);
565 fDDDx = fDDDy = 0;
566 return;
567 }
568 SkASSERT(0); // FIXME: add cubic case
569 }
570
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000571 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000572 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000573 }
574
575 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000576 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000577 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000578
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000579 int start() const {
580 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000581 }
582
583private:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000584 SkScalar fDx;
585 SkScalar fDy;
586 SkScalar fDDx;
587 SkScalar fDDy;
588 SkScalar fDDDx;
589 SkScalar fDDDy;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000590 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000591 int fStart;
592 int fEnd;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000593};
594
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000595static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
596 int angleCount = angles.count();
597 int angleIndex;
598 angleList.setReserve(angleCount);
599 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
600 *angleList.append() = &angles[angleIndex];
601 }
602 QSort<Angle>(angleList.begin(), angleList.end() - 1);
603}
604
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000605// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000606struct Bounds : public SkRect {
607 static bool Intersects(const Bounds& a, const Bounds& b) {
608 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
609 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
610 }
611
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000612 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
613 if (left < fLeft) {
614 fLeft = left;
615 }
616 if (top < fTop) {
617 fTop = top;
618 }
619 if (right > fRight) {
620 fRight = right;
621 }
622 if (bottom > fBottom) {
623 fBottom = bottom;
624 }
625 }
626
627 void add(const Bounds& toAdd) {
628 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
629 }
630
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000631 bool isEmpty() {
632 return fLeft > fRight || fTop > fBottom
633 || fLeft == fRight && fTop == fBottom
634 || isnan(fLeft) || isnan(fRight)
635 || isnan(fTop) || isnan(fBottom);
636 }
637
638 void setCubicBounds(const SkPoint a[4]) {
639 _Rect dRect;
640 Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
641 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
642 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000643 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
644 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000645 }
646
647 void setQuadBounds(const SkPoint a[3]) {
648 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
649 {a[2].fX, a[2].fY}};
650 _Rect dRect;
651 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000652 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
653 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000654 }
655};
656
caryclark@google.com15fa1382012-05-07 20:49:36 +0000657struct Span {
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000658 Segment* fOther;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000659 mutable SkPoint const* fPt; // lazily computed as needed
660 double fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000661 double fOtherT; // value at fOther[fOtherIndex].fT
662 int fOtherIndex; // can't be used during intersection
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000663 int fWindSum; // accumulated from contours surrounding this one
664 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000665 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000666};
667
668class Segment {
669public:
670 Segment() {
671#if DEBUG_DUMP
672 fID = ++gSegmentID;
673#endif
674 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000675
caryclark@google.com9764cc62012-07-12 19:29:45 +0000676 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
677 if (activeAngleInner(index, done, angles)) {
678 return true;
679 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000680 double referenceT = fTs[index].fT;
681 int lesser = index;
682 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000683 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000684 return true;
685 }
686 }
687 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000688 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000689 return true;
690 }
691 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
692 return false;
693 }
694
caryclark@google.com9764cc62012-07-12 19:29:45 +0000695 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000696 Span* span = &fTs[index];
697 Segment* other = span->fOther;
698 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +0000699 return other->activeAngleInner(oIndex, done, angles);
700 }
701
702 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
703 int next = nextSpan(index, 1);
704 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000705 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000706 if (upSpan.fWindValue) {
707 addAngle(angles, index, next);
708 if (upSpan.fDone) {
709 done++;
710 } else if (upSpan.fWindSum != SK_MinS32) {
711 return true;
712 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000713 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000714 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000715 int prev = nextSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000716 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +0000717 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000718 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000719 if (downSpan.fWindValue) {
720 addAngle(angles, index, prev);
721 if (downSpan.fDone) {
722 done++;
723 } else if (downSpan.fWindSum != SK_MinS32) {
724 return true;
725 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000726 }
727 }
728 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000729 }
730
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000731 SkScalar activeTop() const {
732 SkASSERT(!done());
733 int count = fTs.count();
734 SkScalar result = SK_ScalarMax;
735 bool lastDone = true;
736 for (int index = 0; index < count; ++index) {
737 bool done = fTs[index].fDone;
738 if (!done || !lastDone) {
739 SkScalar y = yAtT(index);
740 if (result > y) {
741 result = y;
742 }
743 }
744 lastDone = done;
745 }
746 SkASSERT(result < SK_ScalarMax);
747 return result;
748 }
749
750 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000751 SkASSERT(start != end);
752 SkPoint edge[4];
753 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
754 Angle* angle = angles.append();
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000755 angle->set(edge, fVerb, this, start, end);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000756 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000757
caryclark@google.comcc905052012-07-25 20:59:42 +0000758 void addCancelOutsides(const SkTDArray<double>& outsideTs, Segment& other,
759 double oEnd) {
760 int tIndex = -1;
761 int tCount = fTs.count();
762 int oIndex = -1;
763 int oCount = other.fTs.count();
764 double tStart = outsideTs[0];
765 double oStart = outsideTs[1];
766 do {
767 ++tIndex;
768 } while (tStart - fTs[tIndex].fT >= FLT_EPSILON && tIndex < tCount);
769 int tIndexStart = tIndex;
770 do {
771 ++oIndex;
772 } while (oStart - other.fTs[oIndex].fT >= FLT_EPSILON && oIndex < oCount);
773 int oIndexStart = oIndex;
774 double nextT;
775 do {
776 nextT = fTs[++tIndex].fT;
777 } while (nextT < 1 && nextT - tStart < FLT_EPSILON);
778 double oNextT;
779 do {
780 oNextT = other.fTs[++oIndex].fT;
781 } while (oNextT < 1 && oNextT - oStart < FLT_EPSILON);
782 // at this point, spans before and after are at:
783 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
784 // if tIndexStart == 0, no prior span
785 // if nextT == 1, no following span
786
787 // advance the span with zero winding
788 // if the following span exists (not past the end, non-zero winding)
789 // connect the two edges
790 if (!fTs[tIndexStart].fWindValue) {
791 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
792 #if DEBUG_CONCIDENT
793 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
794 __FUNCTION__, fID, other.fID, tIndexStart - 1,
795 fTs[tIndexStart - 1].fT, xyAtT(tIndexStart - 1).fX,
796 xyAtT(tIndexStart - 1).fY);
797 #endif
798 SkASSERT(0); // incomplete
799 }
800 if (nextT < 1 && fTs[tIndex].fWindValue) {
801 #if DEBUG_CONCIDENT
802 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
803 __FUNCTION__, fID, other.fID, tIndex,
804 fTs[tIndex].fT, xyAtT(tIndex).fX,
805 xyAtT(tIndex).fY);
806 #endif
807 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT);
808 }
809 } else {
810 SkASSERT(!other.fTs[oIndexStart].fWindValue);
811 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
812 #if DEBUG_CONCIDENT
813 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
814 __FUNCTION__, fID, other.fID, oIndexStart - 1,
815 other.fTs[oIndexStart - 1].fT, other.xyAtT(oIndexStart - 1).fX,
816 other.xyAtT(oIndexStart - 1).fY);
817 #endif
818 SkASSERT(0); // incomplete
819 }
820 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
821 #if DEBUG_CONCIDENT
822 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
823 __FUNCTION__, fID, other.fID, oIndex,
824 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
825 other.xyAtT(oIndex).fY);
826 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
827 #endif
828 }
829 }
830 }
831
832 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
833 double oEnd) {
834 // walk this to outsideTs[0]
835 // walk other to outsideTs[1]
836 // if either is > 0, add a pointer to the other, copying adjacent winding
837 int tIndex = -1;
838 int oIndex = -1;
839 double tStart = outsideTs[0];
840 double oStart = outsideTs[1];
841 do {
842 ++tIndex;
843 } while (tStart - fTs[tIndex].fT >= FLT_EPSILON);
844 do {
845 ++oIndex;
846 } while (oStart - other.fTs[oIndex].fT >= FLT_EPSILON);
847 if (tIndex > 0 || oIndex > 0) {
848 addTPair(tStart, other, oStart);
849 }
850 tStart = fTs[tIndex].fT;
851 oStart = other.fTs[oIndex].fT;
852 do {
853 double nextT;
854 do {
855 nextT = fTs[++tIndex].fT;
856 } while (nextT - tStart < FLT_EPSILON);
857 tStart = nextT;
858 do {
859 nextT = other.fTs[++oIndex].fT;
860 } while (nextT - oStart < FLT_EPSILON);
861 oStart = nextT;
862 if (tStart == 1 && oStart == 1) {
863 break;
864 }
865 addTPair(tStart, other, oStart);
866 } while (tStart < 1 && oStart < 1 && oEnd - oStart >= FLT_EPSILON);
867 }
868
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000869 void addCubic(const SkPoint pts[4]) {
870 init(pts, SkPath::kCubic_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000871 fBounds.setCubicBounds(pts);
872 }
873
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000874 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000875 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000876 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000877 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000878 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000879 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000880 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000881 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
882 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
883 if (fVerb > 1) {
884 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
885 }
886 if (fVerb > 2) {
887 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
888 }
889 SkDebugf("\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000890 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000891 switch (fVerb) {
892 case SkPath::kLine_Verb:
893 path.lineTo(edge[1].fX, edge[1].fY);
894 break;
895 case SkPath::kQuad_Verb:
896 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
897 break;
898 case SkPath::kCubic_Verb:
899 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
900 edge[3].fX, edge[3].fY);
901 break;
902 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000903 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000904 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000905 }
906
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000907 void addLine(const SkPoint pts[2]) {
908 init(pts, SkPath::kLine_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000909 fBounds.set(pts, 2);
910 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000911
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000912 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
913 const SkPoint& pt = xyAtT(tIndex);
914 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000915 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000916 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000917 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000918 path.moveTo(pt.fX, pt.fY);
919 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000920 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000921 }
922
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000923 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000924 void addOtherT(int index, double otherT, int otherIndex) {
925 Span& span = fTs[index];
926 span.fOtherT = otherT;
927 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000928 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000929
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000930 void addQuad(const SkPoint pts[3]) {
931 init(pts, SkPath::kQuad_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000932 fBounds.setQuadBounds(pts);
933 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000934
935 // Defer all coincident edge processing until
936 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000937
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000938// no need to be tricky; insert in normal T order
939// resolve overlapping ts when considering coincidence later
940
941 // add non-coincident intersection. Resulting edges are sorted in T.
942 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000943 // FIXME: in the pathological case where there is a ton of intercepts,
944 // binary search?
945 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000946 size_t tCount = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000947 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000948 // OPTIMIZATION: if there are three or more identical Ts, then
949 // the fourth and following could be further insertion-sorted so
950 // that all the edges are clockwise or counterclockwise.
951 // This could later limit segment tests to the two adjacent
952 // neighbors, although it doesn't help with determining which
953 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000954 if (newT < fTs[index].fT) {
955 insertedAt = index;
956 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000957 }
958 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000959 Span* span;
960 if (insertedAt >= 0) {
961 span = fTs.insert(insertedAt);
962 } else {
963 insertedAt = tCount;
964 span = fTs.append();
965 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000966 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000967 span->fOther = other;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000968 span->fPt = NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000969 span->fWindSum = SK_MinS32;
970 span->fWindValue = 1;
971 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000972 ++fDoneSpans;
973 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000974 return insertedAt;
975 }
976
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000977 // set spans from start to end to decrement by one
978 // note this walks other backwards
979 // FIMXE: there's probably an edge case that can be constructed where
980 // two span in one segment are separated by float epsilon on one span but
981 // not the other, if one segment is very small. For this
982 // case the counts asserted below may or may not be enough to separate the
983 // spans. Even if the counts work out, what if the spanw aren't correctly
984 // sorted? It feels better in such a case to match the span's other span
985 // pointer since both coincident segments must contain the same spans.
986 void addTCancel(double startT, double endT, Segment& other,
987 double oStartT, double oEndT) {
988 SkASSERT(endT - startT >= FLT_EPSILON);
989 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
990 int index = 0;
991 while (startT - fTs[index].fT >= FLT_EPSILON) {
992 ++index;
993 }
caryclark@google.comb9738012012-07-03 19:53:30 +0000994 int oIndex = other.fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000995 while (other.fTs[--oIndex].fT - oEndT > -FLT_EPSILON)
996 ;
997 Span* test = &fTs[index];
998 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +0000999 SkTDArray<double> outsideTs;
1000 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001001 do {
1002 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001003 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001004 Span* end = test;
caryclark@google.com18063442012-07-25 12:05:18 +00001005 double startT = end->fT;
1006 double oStartT = oTest->fT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001007 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001008 if (decrement) {
caryclark@google.com18063442012-07-25 12:05:18 +00001009 decrementSpan(end);
caryclark@google.comcc905052012-07-25 20:59:42 +00001010 } else if (track && end->fT < 1 && oStartT < 1) {
caryclark@google.com18063442012-07-25 12:05:18 +00001011 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001012 }
1013 end = &fTs[++index];
1014 } while (end->fT - test->fT < FLT_EPSILON);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001015 Span* oTestStart = oTest;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001016 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001017 if (decrement) {
caryclark@google.com18063442012-07-25 12:05:18 +00001018 other.decrementSpan(oTestStart);
caryclark@google.comcc905052012-07-25 20:59:42 +00001019 } else if (track && oTestStart->fT < 1 && startT < 1) {
caryclark@google.com18063442012-07-25 12:05:18 +00001020 TrackOutside(oOutsideTs, oTestStart->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001021 }
1022 if (!oIndex) {
1023 break;
1024 }
1025 oTestStart = &other.fTs[--oIndex];
1026 } while (oTest->fT - oTestStart->fT < FLT_EPSILON);
1027 test = end;
1028 oTest = oTestStart;
1029 } while (test->fT < endT - FLT_EPSILON);
1030 SkASSERT(!oIndex || oTest->fT <= oStartT - FLT_EPSILON);
caryclark@google.com18063442012-07-25 12:05:18 +00001031 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001032 if (!done() && outsideTs.count()) {
1033 addCancelOutsides(outsideTs, other, oEndT);
caryclark@google.com18063442012-07-25 12:05:18 +00001034 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001035 if (!other.done() && oOutsideTs.count()) {
1036 other.addCancelOutsides(oOutsideTs, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001037 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001038 }
1039
1040 // set spans from start to end to increment the greater by one and decrement
1041 // the lesser
1042 void addTCoincident(double startT, double endT, Segment& other,
1043 double oStartT, double oEndT) {
1044 SkASSERT(endT - startT >= FLT_EPSILON);
1045 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
1046 int index = 0;
1047 while (startT - fTs[index].fT >= FLT_EPSILON) {
1048 ++index;
1049 }
1050 int oIndex = 0;
1051 while (oStartT - other.fTs[oIndex].fT >= FLT_EPSILON) {
1052 ++oIndex;
1053 }
1054 Span* test = &fTs[index];
1055 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001056 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001057 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001058 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001059 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001060 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001061 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001062 bool decrementOther = test->fWindValue >= oTest->fWindValue;
1063 Span* end = test;
1064 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001065 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001066 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001067 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001068 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001069 if (transfer) {
1070 if (decrementOther) {
caryclark@google.com47580692012-07-23 12:14:49 +00001071 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.comb9738012012-07-03 19:53:30 +00001072 ++(end->fWindValue);
caryclark@google.com18063442012-07-25 12:05:18 +00001073 } else if (decrementSpan(end)) {
1074 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001075 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001076 } else if (oTest->fWindValue) {
1077 SkASSERT(!decrementOther);
1078 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1079 TrackOutside(xOutsideTs, end->fT, oStartT);
1080 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001081 }
1082 end = &fTs[++index];
1083 } while (end->fT - test->fT < FLT_EPSILON);
1084 Span* oEnd = oTest;
1085 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001086 if (transfer) {
caryclark@google.com18063442012-07-25 12:05:18 +00001087 if (!decrementOther) {
caryclark@google.com47580692012-07-23 12:14:49 +00001088 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
caryclark@google.comb9738012012-07-03 19:53:30 +00001089 ++(oEnd->fWindValue);
caryclark@google.com18063442012-07-25 12:05:18 +00001090 } else if (other.decrementSpan(oEnd)) {
1091 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001092 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001093 } else if (test->fWindValue) {
1094 SkASSERT(!decrementOther);
1095 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1096 SkASSERT(0); // track for later?
1097 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001098 }
1099 oEnd = &other.fTs[++oIndex];
1100 } while (oEnd->fT - oTest->fT < FLT_EPSILON);
1101 test = end;
1102 oTest = oEnd;
1103 } while (test->fT < endT - FLT_EPSILON);
1104 SkASSERT(oTest->fT < oEndT + FLT_EPSILON);
1105 SkASSERT(oTest->fT > oEndT - FLT_EPSILON);
caryclark@google.comcc905052012-07-25 20:59:42 +00001106 if (!done()) {
1107 if (outsideTs.count()) {
1108 addCoinOutsides(outsideTs, other, oEndT);
1109 }
1110 if (xOutsideTs.count()) {
1111 addCoinOutsides(xOutsideTs, other, oEndT);
1112 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001113 }
1114 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001115 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001116 }
1117 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001118
caryclark@google.comcc905052012-07-25 20:59:42 +00001119 // FIXME: this doesn't prevent the same span from being added twice
1120 // fix in caller, assert here?
caryclark@google.com47580692012-07-23 12:14:49 +00001121 void addTPair(double t, Segment& other, double otherT) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001122 int tCount = fTs.count();
1123 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1124 const Span& span = fTs[tIndex];
1125 if (span.fT > t) {
1126 break;
1127 }
1128 if (span.fT == t && span.fOther == &other && span.fOtherT == otherT) {
1129#if DEBUG_ADD_T_PAIR
1130 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1131 __FUNCTION__, fID, t, other.fID, otherT);
1132#endif
1133 return;
1134 }
1135 }
caryclark@google.com47580692012-07-23 12:14:49 +00001136#if DEBUG_ADD_T_PAIR
1137 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1138 __FUNCTION__, fID, t, other.fID, otherT);
1139#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001140 int insertedAt = addT(t, &other);
1141 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001142 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001143 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com47580692012-07-23 12:14:49 +00001144 Span& newSpan = fTs[insertedAt];
1145 if (insertedAt > 0) {
1146 const Span& lastSpan = fTs[insertedAt - 1];
1147 if (t - lastSpan.fT < FLT_EPSILON) {
1148 int tWind = lastSpan.fWindValue;
1149 newSpan.fWindValue = tWind;
1150 if (!tWind) {
1151 newSpan.fDone = true;
1152 ++fDoneSpans;
1153 }
1154 }
1155 }
1156 int oIndex = newSpan.fOtherIndex;
1157 if (oIndex > 0) {
1158 const Span& lastOther = other.fTs[oIndex - 1];
1159 if (otherT - lastOther.fT < FLT_EPSILON) {
1160 int oWind = lastOther.fWindValue;
1161 Span& otherSpan = other.fTs[oIndex];
1162 otherSpan.fWindValue = oWind;
1163 if (!oWind) {
1164 otherSpan.fDone = true;
1165 ++(other.fDoneSpans);
1166 }
1167 }
1168 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001169 }
1170
1171 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001172 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001173 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1174 addAngle(angles, end, start);
1175 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001176 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001177 int step = SkSign32(end - start);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001178 int tIndex = nextSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001179 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001180 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001181 }
1182 }
caryclark@google.com47580692012-07-23 12:14:49 +00001183
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001184 const Bounds& bounds() const {
1185 return fBounds;
1186 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001187
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001188 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001189 double referenceT = fTs[index].fT;
1190 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001191 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001192 buildAnglesInner(lesser, angles);
1193 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001194 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001195 buildAnglesInner(index, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001196 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001197 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001198
1199 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1200 Span* span = &fTs[index];
1201 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001202 // if there is only one live crossing, and no coincidence, continue
1203 // in the same direction
1204 // if there is coincidence, the only choice may be to reverse direction
1205 // find edge on either side of intersection
1206 int oIndex = span->fOtherIndex;
1207 // if done == -1, prior span has already been processed
1208 int step = 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001209 int next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001210 if (next < 0) {
1211 step = -step;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001212 next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001213 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001214 // add candidate into and away from junction
1215 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001216 }
1217
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001218 bool cancels(const Segment& other) const {
caryclark@google.comb9738012012-07-03 19:53:30 +00001219 SkASSERT(fVerb == SkPath::kLine_Verb);
1220 SkASSERT(other.fVerb == SkPath::kLine_Verb);
1221 SkPoint dxy = fPts[0] - fPts[1];
1222 SkPoint odxy = other.fPts[0] - other.fPts[1];
1223 return dxy.fX * odxy.fX < 0 || dxy.fY * odxy.fY < 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001224 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001225
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001226 // figure out if the segment's ascending T goes clockwise or not
1227 // not enough context to write this as shown
1228 // instead, add all segments meeting at the top
1229 // sort them using buildAngleList
1230 // find the first in the sort
1231 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001232 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001233 SkASSERT(0); // incomplete
1234 return false;
1235 }
1236
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001237 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001238 int bestT = -1;
1239 SkScalar top = bounds().fTop;
1240 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001241 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001242 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001243 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001244 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001245 if (fTs[start].fWindValue == 0) {
1246 continue;
1247 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001248 SkPoint edge[4];
1249 // OPTIMIZE: wrap this so that if start==0 end==fTCount-1 we can
1250 // work with the original data directly
1251 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001252 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001253 Intersections intersections;
1254 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1255 false, intersections);
1256 if (pts == 0) {
1257 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001258 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001259 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1260 // if the intersection is edge on, wait for another one
1261 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001262 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001263 SkASSERT(pts == 1); // FIXME: more code required to disambiguate
1264 SkPoint pt;
1265 double foundT = intersections.fT[0][0];
1266 (*SegmentXYAtT[fVerb])(fPts, foundT, &pt);
1267 if (bestY < pt.fY) {
1268 bestY = pt.fY;
1269 bestT = foundT < 1 ? start : end;
caryclark@google.com47580692012-07-23 12:14:49 +00001270 hitT = fTs[start].fT + (fTs[end].fT - fTs[start].fT) * foundT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001271 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001272 } while (fTs[end].fT != 1);
1273 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001274 }
caryclark@google.com18063442012-07-25 12:05:18 +00001275
1276 bool decrementSpan(Span* span) {
1277 SkASSERT(span->fWindValue > 0);
1278 if (--(span->fWindValue) == 0) {
1279 span->fDone = true;
1280 ++fDoneSpans;
1281 return true;
1282 }
1283 return false;
1284 }
1285
caryclark@google.com15fa1382012-05-07 20:49:36 +00001286 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001287 SkASSERT(fDoneSpans <= fTs.count());
1288 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001289 }
1290
caryclark@google.com47580692012-07-23 12:14:49 +00001291 bool done(const Angle& angle) const {
1292 int start = angle.start();
1293 int end = angle.end();
1294 const Span& mSpan = fTs[SkMin32(start, end)];
1295 return mSpan.fDone;
1296 }
1297
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001298 // so the span needs to contain the pairing info found here
1299 // this should include the winding computed for the edge, and
1300 // what edge it connects to, and whether it is discarded
1301 // (maybe discarded == abs(winding) > 1) ?
1302 // only need derivatives for duration of sorting, add a new struct
1303 // for pairings, remove extra spans that have zero length and
1304 // reference an unused other
1305 // for coincident, the last span on the other may be marked done
1306 // (always?)
1307
1308 // if loop is exhausted, contour may be closed.
1309 // FIXME: pass in close point so we can check for closure
1310
1311 // given a segment, and a sense of where 'inside' is, return the next
1312 // segment. If this segment has an intersection, or ends in multiple
1313 // segments, find the mate that continues the outside.
1314 // note that if there are multiples, but no coincidence, we can limit
1315 // choices to connections in the correct direction
1316
1317 // mark found segments as done
1318
caryclark@google.com15fa1382012-05-07 20:49:36 +00001319 // start is the index of the beginning T of this edge
1320 // it is guaranteed to have an end which describes a non-zero length (?)
1321 // winding -1 means ccw, 1 means cw
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001322 // firstFind allows coincident edges to be treated differently
caryclark@google.com5c286d32012-07-13 11:57:28 +00001323 Segment* findNext(SkTDArray<Span*>& chase, int winding,
caryclark@google.comafe56de2012-07-24 18:11:03 +00001324 int contourWinding, bool firstFind, bool active,
caryclark@google.com0e08a192012-07-13 21:07:52 +00001325 const int startIndex, const int endIndex, int& nextStart,
caryclark@google.comafe56de2012-07-24 18:11:03 +00001326 int& nextEnd, int& spanWinding) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001327
1328 start here;
1329 // winding is a mess
1330 // try to simplify what we got
1331
caryclark@google.comafe56de2012-07-24 18:11:03 +00001332 int flipped = 1;
caryclark@google.come21cb182012-07-23 21:26:31 +00001333 int sumWinding = winding + spanWinding;
caryclark@google.comcc905052012-07-25 20:59:42 +00001334 if (sumWinding == 0 || (false && contourWinding && !firstFind)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00001335 sumWinding = spanWinding;
1336 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00001337 bool insideContour = contourWinding && contourWinding * sumWinding < 0;
caryclark@google.comcc905052012-07-25 20:59:42 +00001338 if (insideContour && (true || !firstFind)) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00001339 sumWinding = contourWinding;
1340 }
1341
caryclark@google.come21cb182012-07-23 21:26:31 +00001342 #if DEBUG_WINDING
1343 SkDebugf("%s winding=%d contourWinding=%d spanWinding=%d sumWinding=%d\n",
1344 __FUNCTION__, winding, contourWinding, spanWinding, sumWinding);
1345 #endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001346 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001347 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001348 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1349 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001350 int step = SkSign32(endIndex - startIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001351 int end = nextSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001352 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001353 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001354 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001355 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001356 // mark the smaller of startIndex, endIndex done, and all adjacent
1357 // spans with the same T value (but not 'other' spans)
caryclark@google.come21cb182012-07-23 21:26:31 +00001358 markDone(SkMin32(startIndex, endIndex), sumWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001359 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001360 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00001361 double startT = other->fTs[nextStart].fT;
1362 nextEnd = nextStart;
1363 do {
1364 nextEnd += step;
1365 } while (fabs(startT - other->fTs[nextEnd].fT) < FLT_EPSILON);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001366 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001367 return other;
1368 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001369 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001370 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001371 SkASSERT(startIndex - endIndex != 0);
1372 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001373 addTwoAngles(startIndex, end, angles);
1374 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001375 SkTDArray<Angle*> sorted;
1376 sortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001377 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001378 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001379 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00001380 #if DEBUG_SORT
caryclark@google.come21cb182012-07-23 21:26:31 +00001381 debugShowSort(sorted, firstIndex, contourWinding, sumWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00001382 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001383 bool doBump = sorted[firstIndex]->firstBump(sumWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001384 #if DEBUG_WINDING
caryclark@google.comafe56de2012-07-24 18:11:03 +00001385 SkDebugf("%s sumWinding=%d sign=%d (%sbump)\n", __FUNCTION__,
1386 sumWinding, sorted[firstIndex]->sign(), doBump ? "" : "no ");
caryclark@google.com0e08a192012-07-13 21:07:52 +00001387 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00001388 bool innerSwap = active && (doBump || insideContour);
caryclark@google.come21cb182012-07-23 21:26:31 +00001389 int startWinding = sumWinding;
1390 // SkASSERT(SkSign32(sumWinding) == SkSign32(winding) || winding == 0);
caryclark@google.comafe56de2012-07-24 18:11:03 +00001391 if (doBump || insideContour) {
caryclark@google.com18063442012-07-25 12:05:18 +00001392 sumWinding -= windBump(sorted[firstIndex]);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001393 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001394 int nextIndex = firstIndex + 1;
1395 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1396 const Angle* foundAngle = NULL;
caryclark@google.com47580692012-07-23 12:14:49 +00001397 bool foundDone = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001398 // iterate through the angle, and compute everyone's winding
1399 bool firstEdge = true;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001400 bool flopped = false;
1401 Segment* nextSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001402 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001403 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001404 nextIndex = 0;
1405 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001406 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00001407 int maxWinding = sumWinding;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001408 nextSegment = nextAngle->segment();
1409 sumWinding -= nextSegment->windBump(nextAngle);
caryclark@google.come21cb182012-07-23 21:26:31 +00001410 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001411 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00001412 SkDebugf("%s maxWinding=%d sumWinding=%d sign=%d\n", __FUNCTION__,
1413 maxWinding, sumWinding, nextAngle->sign());
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001414 #endif
caryclark@google.come21cb182012-07-23 21:26:31 +00001415 if (maxWinding * sumWinding < 0) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001416 flipped = -flipped;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001417 flopped = true;
caryclark@google.com47580692012-07-23 12:14:49 +00001418 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00001419 SkDebugf("flipped sign %d %d\n", maxWinding, sumWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00001420 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001421 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001422 firstEdge = false;
caryclark@google.come21cb182012-07-23 21:26:31 +00001423 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00001424 if (!active) {
caryclark@google.com47580692012-07-23 12:14:49 +00001425 markDone(SkMin32(startIndex, endIndex), startWinding);
1426 nextSegment->markWinding(SkMin32(nextAngle->start(),
1427 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001428 #if DEBUG_WINDING
caryclark@google.com5c286d32012-07-13 11:57:28 +00001429 SkDebugf("%s inactive\n", __FUNCTION__);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001430 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00001431 return NULL;
1432 }
caryclark@google.com47580692012-07-23 12:14:49 +00001433 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001434 foundAngle = nextAngle;
caryclark@google.com47580692012-07-23 12:14:49 +00001435 foundDone = nextSegment->done(*nextAngle);
caryclark@google.comafe56de2012-07-24 18:11:03 +00001436 if (!flopped && maxWinding * startWinding < 0) {
caryclark@google.com47580692012-07-23 12:14:49 +00001437 flipped = -flipped;
1438 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00001439 SkDebugf("flopped sign %d %d\n", maxWinding, startWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00001440 #endif
1441 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001442 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001443 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001444 }
caryclark@google.com0e08a192012-07-13 21:07:52 +00001445 if (!maxWinding && innerSwap && !foundAngle) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001446 if (sumWinding * startWinding < 0 && flipped > 0) {
1447 SkDebugf("%s flip?\n");
1448 // flipped = -flipped;
1449 }
caryclark@google.com0e08a192012-07-13 21:07:52 +00001450 foundAngle = nextAngle;
1451 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001452 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001453 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001454 }
1455 // if the winding is non-zero, nextAngle does not connect to
1456 // current chain. If we haven't done so already, mark the angle
1457 // as done, record the winding value, and mark connected unambiguous
1458 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001459 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.come21cb182012-07-23 21:26:31 +00001460 if (abs(maxWinding) < abs(sumWinding)) {
1461 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001462 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001463 Span* last;
caryclark@google.com0e08a192012-07-13 21:07:52 +00001464 if (foundAngle || innerSwap) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001465 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001466 } else {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001467 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
1468 }
1469 if (last) {
1470 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001471 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001472 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001473 } while (++nextIndex != lastIndex);
caryclark@google.com47580692012-07-23 12:14:49 +00001474 SkASSERT(sorted[firstIndex]->segment() == this);
1475 markDone(SkMin32(startIndex, endIndex), startWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001476 if (!foundAngle) {
1477 return NULL;
1478 }
1479 nextStart = foundAngle->start();
1480 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00001481 nextSegment = foundAngle->segment();
1482 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
1483 SkMin32(nextStart, nextEnd));
1484 #if DEBUG_WINDING
1485 SkDebugf("%s spanWinding=%d\n", __FUNCTION__, spanWinding);
1486 #endif
1487 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001488 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001489
1490 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
1491 int angleCount = sorted.count();
1492 int firstIndex = -1;
1493 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
1494 const Angle* angle = sorted[angleIndex];
1495 if (angle->segment() == this && angle->start() == end &&
1496 angle->end() == start) {
1497 firstIndex = angleIndex;
1498 break;
1499 }
1500 }
1501 return firstIndex;
1502 }
1503
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001504 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001505 void findTooCloseToCall(int /* winding */ ) { // FIXME: winding should be considered
caryclark@google.com15fa1382012-05-07 20:49:36 +00001506 int count = fTs.count();
1507 if (count < 3) { // require t=0, x, 1 at minimum
1508 return;
1509 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001510 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001511 int moCount;
1512 Span* match;
1513 Segment* mOther;
1514 do {
1515 match = &fTs[matchIndex];
1516 mOther = match->fOther;
1517 moCount = mOther->fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001518 if (moCount >= 3) {
1519 break;
1520 }
1521 if (++matchIndex >= count) {
1522 return;
1523 }
1524 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00001525 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001526 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001527 // look for a pair of nearby T values that map to the same (x,y) value
1528 // if found, see if the pair of other segments share a common point. If
1529 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00001530 for (int index = matchIndex + 1; index < count; ++index) {
1531 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001532 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001533 continue;
1534 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001535 Segment* tOther = test->fOther;
1536 int toCount = tOther->fTs.count();
1537 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001538 continue;
1539 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001540 const SkPoint* testPt = &xyAtT(test);
1541 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001542 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001543 moCount = toCount;
1544 match = test;
1545 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001546 matchPt = testPt;
1547 continue;
1548 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001549 int moStart = -1;
1550 int moEnd = -1;
1551 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001552 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001553 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001554 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001555 continue;
1556 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001557 if (moSpan.fOther == this) {
1558 if (moSpan.fOtherT == match->fT) {
1559 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001560 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001561 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001562 continue;
1563 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001564 if (moSpan.fOther == tOther) {
1565 SkASSERT(moEnd == -1);
1566 moEnd = moIndex;
1567 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001568 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001569 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001570 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001571 continue;
1572 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001573 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
1574 if (moStartT == moEndT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001575 continue;
1576 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001577 int toStart = -1;
1578 int toEnd = -1;
1579 double toStartT, toEndT;
1580 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
1581 Span& toSpan = tOther->fTs[toIndex];
1582 if (toSpan.fOther == this) {
1583 if (toSpan.fOtherT == test->fT) {
1584 toStart = toIndex;
1585 toStartT = toSpan.fT;
1586 }
1587 continue;
1588 }
1589 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
1590 SkASSERT(toEnd == -1);
1591 toEnd = toIndex;
1592 toEndT = toSpan.fT;
1593 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001594 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001595 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
1596 if (toStart <= 0 || toEnd <= 0) {
1597 continue;
1598 }
1599 if (toStartT == toEndT) {
1600 continue;
1601 }
1602 // test to see if the segment between there and here is linear
1603 if (!mOther->isLinear(moStart, moEnd)
1604 || !tOther->isLinear(toStart, toEnd)) {
1605 continue;
1606 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001607 // FIXME: defer implementation until the rest works
1608 // this may share code with regular coincident detection
1609 SkASSERT(0);
1610 #if 0
1611 if (flipped) {
1612 mOther->addTCancel(moStart, moEnd, tOther, tStart, tEnd);
1613 } else {
1614 mOther->addTCoincident(moStart, moEnd, tOther, tStart, tEnd);
1615 }
1616 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001617 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001618 }
1619
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001620 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
1621 // and use more concise logic like the old edge walker code?
1622 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001623 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001624 // iterate through T intersections and return topmost
1625 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001626 SkASSERT(!done());
1627 int firstT;
1628 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001629 SkPoint topPt;
1630 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001631 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001632 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001633 bool lastDone = true;
1634 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001635 const Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001636 if (!span.fDone || !lastDone) {
1637 const SkPoint& intercept = xyAtT(&span);
1638 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
1639 && topPt.fX > intercept.fX)) {
1640 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001641 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001642 } else if (topPt == intercept) {
1643 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001644 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001645 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001646 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001647 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001648 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001649 int step = 1;
1650 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001651 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001652 step = -1;
1653 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001654 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001655 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001656 // if the topmost T is not on end, or is three-way or more, find left
1657 // look for left-ness from tLeft to firstT (matching y of other)
1658 SkTDArray<Angle> angles;
1659 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001660 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001661 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001662 SkTDArray<Angle*> sorted;
1663 sortAngles(angles, sorted);
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001664 // skip edges that have already been processed
1665 firstT = -1;
1666 Segment* leftSegment;
1667 do {
1668 const Angle* angle = sorted[++firstT];
1669 leftSegment = angle->segment();
1670 tIndex = angle->end();
1671 endIndex = angle->start();
1672 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001673 return leftSegment;
1674 }
1675
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001676 // FIXME: not crazy about this
1677 // when the intersections are performed, the other index is into an
1678 // incomplete array. as the array grows, the indices become incorrect
1679 // while the following fixes the indices up again, it isn't smart about
1680 // skipping segments whose indices are already correct
1681 // assuming we leave the code that wrote the index in the first place
1682 void fixOtherTIndex() {
1683 int iCount = fTs.count();
1684 for (int i = 0; i < iCount; ++i) {
1685 Span& iSpan = fTs[i];
1686 double oT = iSpan.fOtherT;
1687 Segment* other = iSpan.fOther;
1688 int oCount = other->fTs.count();
1689 for (int o = 0; o < oCount; ++o) {
1690 Span& oSpan = other->fTs[o];
1691 if (oT == oSpan.fT && this == oSpan.fOther) {
1692 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001693 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001694 }
1695 }
1696 }
1697 }
1698
caryclark@google.com495f8e42012-05-31 13:13:11 +00001699 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001700 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001701 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001702 SkASSERT(end >= 0);
1703 if (multipleSpans(end)) {
1704 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001705 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001706 const Span& endSpan = fTs[end];
1707 Segment* other = endSpan.fOther;
1708 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001709 int otherEnd = other->nextSpan(index, step);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001710 Span* last = other->innerChaseDone(index, step, winding);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001711 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001712 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001713 }
1714
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001715 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001716 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001717 SkASSERT(end >= 0);
1718 if (multipleSpans(end)) {
1719 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001720 }
1721 const Span& endSpan = fTs[end];
1722 Segment* other = endSpan.fOther;
1723 index = endSpan.fOtherIndex;
1724 int otherEnd = other->nextSpan(index, step);
1725 int min = SkMin32(index, otherEnd);
1726 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00001727 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001728 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001729 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001730 Span* last = other->innerChaseWinding(index, step, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001731 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001732 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001733 }
1734
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001735 void init(const SkPoint pts[], SkPath::Verb verb) {
1736 fPts = pts;
1737 fVerb = verb;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001738 fDoneSpans = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001739 }
1740
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001741 bool intersected() const {
1742 return fTs.count() > 0;
1743 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001744
1745 bool isLinear(int start, int end) const {
1746 if (fVerb == SkPath::kLine_Verb) {
1747 return true;
1748 }
1749 if (fVerb == SkPath::kQuad_Verb) {
1750 SkPoint qPart[3];
1751 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
1752 return QuadIsLinear(qPart);
1753 } else {
1754 SkASSERT(fVerb == SkPath::kCubic_Verb);
1755 SkPoint cPart[4];
1756 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
1757 return CubicIsLinear(cPart);
1758 }
1759 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001760
1761 // OPTIMIZE: successive calls could start were the last leaves off
1762 // or calls could specialize to walk forwards or backwards
1763 bool isMissing(double startT) const {
1764 size_t tCount = fTs.count();
1765 for (size_t index = 0; index < tCount; ++index) {
1766 if (fabs(startT - fTs[index].fT) < FLT_EPSILON) {
1767 return false;
1768 }
1769 }
1770 return true;
1771 }
1772
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001773 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001774 int count = fTs.count();
1775 if (count == 2) {
1776 return true;
1777 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001778 double t = fTs[end].fT;
1779 if (t < FLT_EPSILON) {
1780 return fTs[1].fT >= FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001781 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001782 if (t > 1 - FLT_EPSILON) {
1783 return fTs[count - 2].fT <= 1 - FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001784 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001785 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001786 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001787
1788 bool isHorizontal() const {
1789 return fBounds.fTop == fBounds.fBottom;
1790 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001791
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001792 bool isVertical() const {
1793 return fBounds.fLeft == fBounds.fRight;
1794 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001795
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001796 SkScalar leftMost(int start, int end) const {
1797 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
1798 }
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001799
caryclark@google.com495f8e42012-05-31 13:13:11 +00001800 // this span is excluded by the winding rule -- chase the ends
1801 // as long as they are unambiguous to mark connections as done
1802 // and give them the same winding value
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001803 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001804 int index = angle->start();
1805 int endIndex = angle->end();
1806 int step = SkSign32(endIndex - index);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001807 Span* last = innerChaseDone(index, step, winding);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001808 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001809 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001810 }
1811
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001812 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001813 int index = angle->start();
1814 int endIndex = angle->end();
1815 int min = SkMin32(index, endIndex);
1816 int step = SkSign32(endIndex - index);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001817 Span* last = innerChaseWinding(index, step, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001818 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001819 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001820 }
1821
caryclark@google.com495f8e42012-05-31 13:13:11 +00001822 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001823 // This may be called when the segment is already marked done. While this
1824 // wastes time, it shouldn't do any more than spin through the T spans.
1825 // OPTIMIZATION: abort on first done found (assuming that this code is
1826 // always called to mark segments done).
caryclark@google.com495f8e42012-05-31 13:13:11 +00001827 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001828 // SkASSERT(!done());
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001829 double referenceT = fTs[index].fT;
1830 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001831 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001832 Span& span = fTs[lesser];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001833 if (span.fDone) {
1834 continue;
1835 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001836 #if DEBUG_MARK_DONE
1837 const SkPoint& pt = xyAtT(&span);
1838 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1839 __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
1840 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001841 span.fDone = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001842 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001843 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001844 span.fWindSum = winding;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001845 fDoneSpans++;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001846 }
1847 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001848 Span& span = fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001849 // SkASSERT(!span.fDone);
1850 if (span.fDone) {
1851 continue;
1852 }
1853 #if DEBUG_MARK_DONE
1854 const SkPoint& pt = xyAtT(&span);
1855 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1856 __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
1857 #endif
1858 span.fDone = true;
1859 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001860 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001861 span.fWindSum = winding;
1862 fDoneSpans++;
1863 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
1864 }
1865
1866 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00001867 // SkASSERT(!done());
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001868 double referenceT = fTs[index].fT;
1869 int lesser = index;
1870 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
1871 Span& span = fTs[lesser];
1872 if (span.fDone) {
1873 continue;
1874 }
caryclark@google.com47580692012-07-23 12:14:49 +00001875 // SkASSERT(span.fWindValue == 1 || winding == 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001876 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1877 #if DEBUG_MARK_DONE
1878 const SkPoint& pt = xyAtT(&span);
1879 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1880 __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
1881 #endif
caryclark@google.com47580692012-07-23 12:14:49 +00001882 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001883 span.fWindSum = winding;
1884 }
1885 do {
1886 Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001887 // SkASSERT(!span.fDone || span.fCoincident);
1888 if (span.fDone) {
1889 continue;
1890 }
caryclark@google.com47580692012-07-23 12:14:49 +00001891 // SkASSERT(span.fWindValue == 1 || winding == 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001892 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1893 #if DEBUG_MARK_DONE
1894 const SkPoint& pt = xyAtT(&span);
1895 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1896 __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
1897 #endif
caryclark@google.com47580692012-07-23 12:14:49 +00001898 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001899 span.fWindSum = winding;
1900 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001901 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001902
caryclark@google.com9764cc62012-07-12 19:29:45 +00001903 // return span if when chasing, two or more radiating spans are not done
1904 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
1905 // candidate and the remaining spans have windValue == 0 (canceled by
1906 // coincidence). The coincident edges could either be removed altogether,
1907 // or this code could be more complicated in detecting this case. Worth it?
1908 bool multipleSpans(int end) const {
1909 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001910 }
1911
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001912 // This has callers for two different situations: one establishes the end
1913 // of the current span, and one establishes the beginning of the next span
1914 // (thus the name). When this is looking for the end of the current span,
1915 // coincidence is found when the beginning Ts contain -step and the end
1916 // contains step. When it is looking for the beginning of the next, the
1917 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001918 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001919 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001920 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001921 int count = fTs.count();
1922 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001923 while (step > 0 ? ++to < count : --to >= 0) {
1924 const Span& span = fTs[to];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001925 if ((step > 0 ? span.fT - fromSpan.fT : fromSpan.fT - span.fT) < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001926 continue;
1927 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001928 return to;
1929 }
1930 return -1;
1931 }
1932
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001933 const SkPoint* pts() const {
1934 return fPts;
1935 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001936
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001937 void reset() {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001938 init(NULL, (SkPath::Verb) -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001939 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1940 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001941 }
1942
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001943 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001944 const Span& span(int tIndex) const {
1945 return fTs[tIndex];
1946 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001947
1948 int spanSign(int startIndex, int endIndex) const {
1949 return startIndex < endIndex ? -fTs[startIndex].fWindValue :
1950 fTs[endIndex].fWindValue;
1951 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001952
1953 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001954 double t(int tIndex) const {
1955 return fTs[tIndex].fT;
1956 }
caryclark@google.com18063442012-07-25 12:05:18 +00001957
1958 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
1959 double start) {
1960 int outCount = outsideTs.count();
1961 if (outCount == 0 || end - outsideTs[outCount - 2] >= FLT_EPSILON) {
1962 *outsideTs.append() = end;
1963 *outsideTs.append() = start;
1964 }
1965 }
1966
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001967 void updatePts(const SkPoint pts[]) {
1968 fPts = pts;
1969 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001970
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001971 SkPath::Verb verb() const {
1972 return fVerb;
1973 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001974
caryclark@google.comafe56de2012-07-24 18:11:03 +00001975 int windBump(const Angle* angle) const {
1976 SkASSERT(angle->segment() == this);
1977 const Span& span = fTs[SkMin32(angle->start(), angle->end())];
1978 int result = angle->sign() * span.fWindValue;
1979#if DEBUG_WIND_BUMP
1980 SkDebugf("%s bump=%d\n", __FUNCTION__, result);
1981#endif
1982 return result;
1983 }
1984
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001985 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001986 return fTs[tIndex].fWindSum;
1987 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001988
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001989 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001990 int start = angle->start();
1991 int end = angle->end();
1992 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001993 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001994 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001995
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001996 int windValue(int tIndex) const {
1997 return fTs[tIndex].fWindValue;
1998 }
1999
2000 int windValue(const Angle* angle) const {
2001 int start = angle->start();
2002 int end = angle->end();
2003 int index = SkMin32(start, end);
2004 return windValue(index);
2005 }
2006
2007 SkScalar xAtT(const Span* span) const {
2008 return xyAtT(span).fX;
2009 }
2010
2011 const SkPoint& xyAtT(int index) const {
2012 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002013 }
2014
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002015 const SkPoint& xyAtT(const Span* span) const {
2016 if (!span->fPt) {
2017 if (span->fT == 0) {
2018 span->fPt = &fPts[0];
2019 } else if (span->fT == 1) {
2020 span->fPt = &fPts[fVerb];
2021 } else {
2022 SkPoint* pt = fIntersections.append();
2023 (*SegmentXYAtT[fVerb])(fPts, span->fT, pt);
2024 span->fPt = pt;
2025 }
2026 }
2027 return *span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002028 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002029
2030 SkScalar yAtT(int index) const {
2031 return yAtT(&fTs[index]);
2032 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002033
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002034 SkScalar yAtT(const Span* span) const {
2035 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002036 }
2037
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002038#if DEBUG_DUMP
2039 void dump() const {
2040 const char className[] = "Segment";
2041 const int tab = 4;
2042 for (int i = 0; i < fTs.count(); ++i) {
2043 SkPoint out;
2044 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
2045 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002046 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002047 tab + sizeof(className), className, fID,
2048 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002049 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002050 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002051 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002052 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00002053 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002054 }
2055#endif
2056
caryclark@google.com47580692012-07-23 12:14:49 +00002057#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00002058 // assert if pair has not already been added
2059 void debugAddTPair(double t, const Segment& other, double otherT) {
2060 for (int i = 0; i < fTs.count(); ++i) {
2061 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
2062 return;
2063 }
2064 }
2065 SkASSERT(0);
2066 }
2067#endif
2068
2069#if DEBUG_CONCIDENT
caryclark@google.com47580692012-07-23 12:14:49 +00002070 void debugShowTs() {
2071 SkDebugf("%s %d", __FUNCTION__, fID);
2072 for (int i = 0; i < fTs.count(); ++i) {
2073 SkDebugf(" [o=%d %1.9g (%1.9g,%1.9g) w=%d]", fTs[i].fOther->fID,
2074 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
2075 }
2076 SkDebugf("\n");
2077 }
2078#endif
2079
caryclark@google.com027de222012-07-12 12:52:50 +00002080#if DEBUG_ACTIVE_SPANS
2081 void debugShowActiveSpans(int contourID, int segmentIndex) {
2082 if (done()) {
2083 return;
2084 }
2085 for (int i = 0; i < fTs.count(); ++i) {
2086 if (fTs[i].fDone) {
2087 continue;
2088 }
2089 SkDebugf("%s contour=%d segment=%d (%d)", __FUNCTION__, contourID,
2090 segmentIndex, fID);
2091 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2092 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2093 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2094 }
2095 const Span* span = &fTs[i];
2096 SkDebugf(") fT=%d (%1.9g) (%1.9g,%1.9g)", i, fTs[i].fT,
2097 xAtT(span), yAtT(i));
2098 const Segment* other = fTs[i].fOther;
2099 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d", other->fID,
2100 fTs[i].fOtherT, fTs[i].fOtherIndex);
2101 SkDebugf(" windSum=%d windValue=%d\n", fTs[i].fWindSum,
2102 fTs[i].fWindValue);
2103 }
2104 }
2105#endif
2106
caryclark@google.com47580692012-07-23 12:14:49 +00002107#if DEBUG_SORT
caryclark@google.come21cb182012-07-23 21:26:31 +00002108 void debugShowSort(const SkTDArray<Angle*>& angles, int first,
2109 int contourWinding, int sumWinding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002110 SkASSERT(angles[first]->segment() == this);
caryclark@google.comcc905052012-07-25 20:59:42 +00002111 bool doBump = angles[first]->firstBump(sumWinding);
caryclark@google.comafe56de2012-07-24 18:11:03 +00002112 bool insideContour = contourWinding && contourWinding * sumWinding < 0;
2113 int windSum = insideContour ? contourWinding : sumWinding;
2114 int lastSum = windSum;
2115 if (insideContour || doBump) {
2116 windSum -= windBump(angles[first]);
2117 } else {
2118 lastSum += windBump(angles[first]);
caryclark@google.com47580692012-07-23 12:14:49 +00002119 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002120 int index = first;
2121 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00002122 do {
2123 const Angle& angle = *angles[index];
2124 const Segment& segment = *angle.segment();
2125 int start = angle.start();
2126 int end = angle.end();
2127 const Span& sSpan = segment.fTs[start];
2128 const Span& eSpan = segment.fTs[end];
2129 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.comafe56de2012-07-24 18:11:03 +00002130 if (firstTime) {
2131 firstTime = false;
2132 } else {
2133 lastSum = windSum;
2134 windSum -= segment.windBump(&angle);
2135 }
caryclark@google.com47580692012-07-23 12:14:49 +00002136 SkDebugf("%s [%d] id=%d start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
2137 " sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
2138 __FUNCTION__, index, segment.fID, start, segment.xAtT(&sSpan),
2139 segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
2140 segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
2141 lastSum, windSum, abs(lastSum) > abs(windSum) ? lastSum :
2142 windSum, mSpan.fDone);
2143 ++index;
2144 if (index == angles.count()) {
2145 index = 0;
2146 }
2147 } while (index != first);
2148 }
2149#endif
2150
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002151private:
2152 const SkPoint* fPts;
2153 SkPath::Verb fVerb;
2154 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002155 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002156 // OPTIMIZATION:if intersections array is a pointer, the it could only
2157 // be allocated as needed instead of always initialized -- though maybe
2158 // the initialization is lightweight enough that it hardly matters
2159 mutable SkTDArray<SkPoint> fIntersections;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002160 int fDoneSpans; // used for quick check that segment is finished
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002161#if DEBUG_DUMP
2162 int fID;
2163#endif
2164};
2165
caryclark@google.comb9738012012-07-03 19:53:30 +00002166class Contour;
2167
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002168struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00002169 Contour* fContours[2];
2170 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002171 double fTs[2][2];
2172};
2173
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002174class Contour {
2175public:
2176 Contour() {
2177 reset();
2178#if DEBUG_DUMP
2179 fID = ++gContourID;
2180#endif
2181 }
2182
2183 bool operator<(const Contour& rh) const {
2184 return fBounds.fTop == rh.fBounds.fTop
2185 ? fBounds.fLeft < rh.fBounds.fLeft
2186 : fBounds.fTop < rh.fBounds.fTop;
2187 }
2188
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002189 void addCoincident(int index, Contour* other, int otherIndex,
2190 const Intersections& ts, bool swap) {
2191 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00002192 coincidence.fContours[0] = this;
2193 coincidence.fContours[1] = other;
2194 coincidence.fSegments[0] = index;
2195 coincidence.fSegments[1] = otherIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002196 coincidence.fTs[swap][0] = ts.fT[0][0];
2197 coincidence.fTs[swap][1] = ts.fT[0][1];
2198 coincidence.fTs[!swap][0] = ts.fT[1][0];
2199 coincidence.fTs[!swap][1] = ts.fT[1][1];
2200 }
2201
2202 void addCross(const Contour* crosser) {
2203#ifdef DEBUG_CROSS
2204 for (int index = 0; index < fCrosses.count(); ++index) {
2205 SkASSERT(fCrosses[index] != crosser);
2206 }
2207#endif
2208 *fCrosses.append() = crosser;
2209 }
2210
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002211 void addCubic(const SkPoint pts[4]) {
2212 fSegments.push_back().addCubic(pts);
2213 fContainsCurves = true;
2214 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002215
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002216 int addLine(const SkPoint pts[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002217 fSegments.push_back().addLine(pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002218 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002219 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002220
2221 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
2222 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
2223 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002224
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002225 int addQuad(const SkPoint pts[3]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002226 fSegments.push_back().addQuad(pts);
2227 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002228 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002229 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002230
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002231 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
2232 containsIntercepts();
2233 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
2234 }
2235
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002236 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002237 return fBounds;
2238 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002239
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002240 void complete() {
2241 setBounds();
2242 fContainsIntercepts = false;
2243 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002244
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002245 void containsIntercepts() {
2246 fContainsIntercepts = true;
2247 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002248
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002249 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
2250 int &tIndex, double& hitT) {
2251 int segmentCount = fSegments.count();
2252 const Segment* bestSegment = NULL;
2253 for (int test = 0; test < segmentCount; ++test) {
2254 Segment* testSegment = &fSegments[test];
2255 const SkRect& bounds = testSegment->bounds();
2256 if (bounds.fTop < bestY) {
2257 continue;
2258 }
2259 if (bounds.fTop > basePt.fY) {
2260 continue;
2261 }
2262 if (bounds.fLeft > basePt.fX) {
2263 continue;
2264 }
2265 if (bounds.fRight < basePt.fX) {
2266 continue;
2267 }
2268 double testHitT;
2269 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
2270 if (testT >= 0) {
2271 bestSegment = testSegment;
2272 tIndex = testT;
2273 hitT = testHitT;
2274 }
2275 }
2276 return bestSegment;
2277 }
2278
2279 bool crosses(const Contour* crosser) const {
2280 if (this == crosser) {
2281 return true;
2282 }
2283 for (int index = 0; index < fCrosses.count(); ++index) {
2284 if (fCrosses[index] == crosser) {
2285 return true;
2286 }
2287 }
2288 return false;
2289 }
2290
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002291 void findTooCloseToCall(int winding) {
2292 int segmentCount = fSegments.count();
2293 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
2294 fSegments[sIndex].findTooCloseToCall(winding);
2295 }
2296 }
2297
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002298 void fixOtherTIndex() {
2299 int segmentCount = fSegments.count();
2300 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
2301 fSegments[sIndex].fixOtherTIndex();
2302 }
2303 }
2304
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002305 void reset() {
2306 fSegments.reset();
2307 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002308 fContainsCurves = fContainsIntercepts = false;
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002309 fWindingSum = SK_MinS32;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002310 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002311
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002312 void resolveCoincidence(int winding) {
2313 int count = fCoincidences.count();
2314 for (int index = 0; index < count; ++index) {
2315 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00002316 Contour* thisContour = coincidence.fContours[0];
2317 Contour* otherContour = coincidence.fContours[1];
2318 int thisIndex = coincidence.fSegments[0];
2319 int otherIndex = coincidence.fSegments[1];
2320 Segment& thisOne = thisContour->fSegments[thisIndex];
2321 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00002322 #if DEBUG_CONCIDENT
2323 thisOne.debugShowTs();
2324 other.debugShowTs();
2325 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002326 double startT = coincidence.fTs[0][0];
2327 double endT = coincidence.fTs[0][1];
2328 if (startT > endT) {
2329 SkTSwap<double>(startT, endT);
2330 }
2331 SkASSERT(endT - startT >= FLT_EPSILON);
2332 double oStartT = coincidence.fTs[1][0];
2333 double oEndT = coincidence.fTs[1][1];
2334 if (oStartT > oEndT) {
2335 SkTSwap<double>(oStartT, oEndT);
2336 }
2337 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
caryclark@google.comb9738012012-07-03 19:53:30 +00002338 if (winding > 0 || thisOne.cancels(other)) {
2339 // make sure startT and endT have t entries
2340 if (thisOne.isMissing(startT) || other.isMissing(oEndT)) {
2341 thisOne.addTPair(startT, other, oEndT);
2342 }
2343 if (thisOne.isMissing(endT) || other.isMissing(oStartT)) {
2344 other.addTPair(oStartT, thisOne, endT);
2345 }
2346 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002347 } else {
caryclark@google.comb9738012012-07-03 19:53:30 +00002348 if (thisOne.isMissing(startT) || other.isMissing(oStartT)) {
2349 thisOne.addTPair(startT, other, oStartT);
2350 }
2351 if (thisOne.isMissing(endT) || other.isMissing(oEndT)) {
2352 other.addTPair(oEndT, thisOne, endT);
2353 }
2354 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002355 }
caryclark@google.com47580692012-07-23 12:14:49 +00002356 #if DEBUG_CONCIDENT
2357 thisOne.debugShowTs();
2358 other.debugShowTs();
2359 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002360 }
2361 }
2362
2363 const SkTArray<Segment>& segments() {
2364 return fSegments;
2365 }
2366
2367 void setWinding(int winding) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002368 SkASSERT(fWindingSum < 0 || fWindingSum == winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002369 fWindingSum = winding;
2370 }
2371
caryclark@google.com15fa1382012-05-07 20:49:36 +00002372 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
2373 // we need to sort and walk edges in y, but that on the surface opens the
2374 // same can of worms as before. But then, this is a rough sort based on
2375 // segments' top, and not a true sort, so it could be ameniable to regular
2376 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002377 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002378 int segmentCount = fSegments.count();
2379 SkASSERT(segmentCount > 0);
2380 int best = -1;
2381 Segment* bestSegment = NULL;
2382 while (++best < segmentCount) {
2383 Segment* testSegment = &fSegments[best];
2384 if (testSegment->done()) {
2385 continue;
2386 }
2387 bestSegment = testSegment;
2388 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002389 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002390 if (!bestSegment) {
2391 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002392 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002393 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002394 for (int test = best + 1; test < segmentCount; ++test) {
2395 Segment* testSegment = &fSegments[test];
2396 if (testSegment->done()) {
2397 continue;
2398 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002399 if (testSegment->bounds().fTop > bestTop) {
2400 continue;
2401 }
2402 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002403 if (bestTop > testTop) {
2404 bestTop = testTop;
2405 bestSegment = testSegment;
2406 }
2407 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002408 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002409 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002410 }
2411
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002412 int updateSegment(int index, const SkPoint* pts) {
2413 Segment& segment = fSegments[index];
2414 segment.updatePts(pts);
2415 return segment.verb() + 1;
2416 }
2417
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002418 int windSum() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002419 if (fWindingSum >= 0) {
2420 return fWindingSum;
2421 }
2422 // check peers
2423 int count = fCrosses.count();
2424 for (int index = 0; index < count; ++index) {
2425 const Contour* crosser = fCrosses[index];
2426 if (0 <= crosser->fWindingSum) {
2427 fWindingSum = crosser->fWindingSum;
2428 break;
2429 }
2430 }
2431 return fWindingSum;
2432 }
2433
2434#if DEBUG_TEST
2435 SkTArray<Segment>& debugSegments() {
2436 return fSegments;
2437 }
2438#endif
2439
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002440#if DEBUG_DUMP
2441 void dump() {
2442 int i;
2443 const char className[] = "Contour";
2444 const int tab = 4;
2445 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
2446 for (i = 0; i < fSegments.count(); ++i) {
2447 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
2448 className, i);
2449 fSegments[i].dump();
2450 }
2451 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
2452 tab + sizeof(className), className,
2453 fBounds.fLeft, fBounds.fTop,
2454 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002455 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
2456 className, fContainsIntercepts);
2457 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
2458 className, fContainsCurves);
2459 }
2460#endif
2461
caryclark@google.com027de222012-07-12 12:52:50 +00002462#if DEBUG_ACTIVE_SPANS
2463 void debugShowActiveSpans() {
2464 for (int index = 0; index < fSegments.count(); ++index) {
2465 fSegments[index].debugShowActiveSpans(fID, index);
2466 }
2467 }
2468#endif
2469
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002470protected:
2471 void setBounds() {
2472 int count = fSegments.count();
2473 if (count == 0) {
2474 SkDebugf("%s empty contour\n", __FUNCTION__);
2475 SkASSERT(0);
2476 // FIXME: delete empty contour?
2477 return;
2478 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002479 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002480 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002481 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002482 }
2483 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002484
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002485private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002486 SkTArray<Segment> fSegments;
2487 SkTDArray<Coincidence> fCoincidences;
2488 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002489 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002490 bool fContainsIntercepts;
2491 bool fContainsCurves;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002492 int fWindingSum; // initial winding number outside
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002493#if DEBUG_DUMP
2494 int fID;
2495#endif
2496};
2497
2498class EdgeBuilder {
2499public:
2500
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002501EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002502 : fPath(path)
2503 , fCurrentContour(NULL)
2504 , fContours(contours)
2505{
2506#if DEBUG_DUMP
2507 gContourID = 0;
2508 gSegmentID = 0;
2509#endif
2510 walk();
2511}
2512
2513protected:
2514
2515void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002516 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002517 fCurrentContour->complete();
2518 fCurrentContour = NULL;
2519 }
2520}
2521
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002522void walk() {
2523 // FIXME:remove once we can access path pts directly
2524 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
2525 SkPoint pts[4];
2526 SkPath::Verb verb;
2527 do {
2528 verb = iter.next(pts);
2529 *fPathVerbs.append() = verb;
2530 if (verb == SkPath::kMove_Verb) {
2531 *fPathPts.append() = pts[0];
2532 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
2533 fPathPts.append(verb, &pts[1]);
2534 }
2535 } while (verb != SkPath::kDone_Verb);
2536 // FIXME: end of section to remove once path pts are accessed directly
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002537
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002538 SkPath::Verb reducedVerb;
2539 uint8_t* verbPtr = fPathVerbs.begin();
2540 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002541 const SkPoint* finalCurveStart = NULL;
2542 const SkPoint* finalCurveEnd = NULL;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002543 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
2544 switch (verb) {
2545 case SkPath::kMove_Verb:
2546 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002547 if (!fCurrentContour) {
2548 fCurrentContour = fContours.push_back_n(1);
2549 finalCurveEnd = pointsPtr++;
2550 *fExtra.append() = -1; // start new contour
2551 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002552 continue;
2553 case SkPath::kLine_Verb:
2554 // skip degenerate points
2555 if (pointsPtr[-1].fX != pointsPtr[0].fX
2556 || pointsPtr[-1].fY != pointsPtr[0].fY) {
2557 fCurrentContour->addLine(&pointsPtr[-1]);
2558 }
2559 break;
2560 case SkPath::kQuad_Verb:
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002561
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002562 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
2563 if (reducedVerb == 0) {
2564 break; // skip degenerate points
2565 }
2566 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002567 *fExtra.append() =
2568 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002569 break;
2570 }
2571 fCurrentContour->addQuad(&pointsPtr[-1]);
2572 break;
2573 case SkPath::kCubic_Verb:
2574 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
2575 if (reducedVerb == 0) {
2576 break; // skip degenerate points
2577 }
2578 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002579 *fExtra.append() =
2580 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002581 break;
2582 }
2583 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002584 *fExtra.append() =
2585 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002586 break;
2587 }
2588 fCurrentContour->addCubic(&pointsPtr[-1]);
2589 break;
2590 case SkPath::kClose_Verb:
2591 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002592 if (finalCurveStart && finalCurveEnd
2593 && *finalCurveStart != *finalCurveEnd) {
2594 *fReducePts.append() = *finalCurveStart;
2595 *fReducePts.append() = *finalCurveEnd;
2596 *fExtra.append() =
2597 fCurrentContour->addLine(fReducePts.end() - 2);
2598 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002599 complete();
2600 continue;
2601 default:
2602 SkDEBUGFAIL("bad verb");
2603 return;
2604 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002605 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002606 pointsPtr += verb;
2607 SkASSERT(fCurrentContour);
2608 }
2609 complete();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002610 if (fCurrentContour && !fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002611 fContours.pop_back();
2612 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002613 // correct pointers in contours since fReducePts may have moved as it grew
2614 int cIndex = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002615 int extraCount = fExtra.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002616 SkASSERT(extraCount == 0 || fExtra[0] == -1);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002617 int eIndex = 0;
2618 int rIndex = 0;
2619 while (++eIndex < extraCount) {
2620 int offset = fExtra[eIndex];
2621 if (offset < 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002622 ++cIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002623 continue;
2624 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002625 fCurrentContour = &fContours[cIndex];
2626 rIndex += fCurrentContour->updateSegment(offset - 1,
2627 &fReducePts[rIndex]);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002628 }
2629 fExtra.reset(); // we're done with this
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002630}
2631
2632private:
2633 const SkPath& fPath;
2634 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002635 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002636 Contour* fCurrentContour;
2637 SkTArray<Contour>& fContours;
2638 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002639 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002640};
2641
2642class Work {
2643public:
2644 enum SegmentType {
2645 kHorizontalLine_Segment = -1,
2646 kVerticalLine_Segment = 0,
2647 kLine_Segment = SkPath::kLine_Verb,
2648 kQuad_Segment = SkPath::kQuad_Verb,
2649 kCubic_Segment = SkPath::kCubic_Verb,
2650 };
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002651
2652 void addCoincident(Work& other, const Intersections& ts, bool swap) {
2653 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
2654 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002655
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002656 // FIXME: does it make sense to write otherIndex now if we're going to
2657 // fix it up later?
2658 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002659 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002660 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002661
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002662 // Avoid collapsing t values that are close to the same since
2663 // we walk ts to describe consecutive intersections. Since a pair of ts can
2664 // be nearly equal, any problems caused by this should be taken care
2665 // of later.
2666 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002667 int addT(double newT, const Work& other) {
2668 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002669 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002670
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002671 bool advance() {
2672 return ++fIndex < fLast;
2673 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002674
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002675 SkScalar bottom() const {
2676 return bounds().fBottom;
2677 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002678
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002679 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002680 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002681 }
2682
2683 const SkPoint* cubic() const {
2684 return fCubic;
2685 }
2686
2687 void init(Contour* contour) {
2688 fContour = contour;
2689 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002690 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002691 }
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002692
2693 bool isAdjacent(const Work& next) {
2694 return fContour == next.fContour && fIndex + 1 == next.fIndex;
2695 }
2696
2697 bool isFirstLast(const Work& next) {
2698 return fContour == next.fContour && fIndex == 0
2699 && next.fIndex == fLast - 1;
2700 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002701
2702 SkScalar left() const {
2703 return bounds().fLeft;
2704 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002705
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002706 void promoteToCubic() {
2707 fCubic[0] = pts()[0];
2708 fCubic[2] = pts()[1];
2709 fCubic[3] = pts()[2];
2710 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
2711 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
2712 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
2713 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
2714 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002715
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002716 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002717 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002718 }
2719
2720 SkScalar right() const {
2721 return bounds().fRight;
2722 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002723
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002724 ptrdiff_t segmentIndex() const {
2725 return fIndex;
2726 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002727
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002728 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002729 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002730 SegmentType type = (SegmentType) segment.verb();
2731 if (type != kLine_Segment) {
2732 return type;
2733 }
2734 if (segment.isHorizontal()) {
2735 return kHorizontalLine_Segment;
2736 }
2737 if (segment.isVertical()) {
2738 return kVerticalLine_Segment;
2739 }
2740 return kLine_Segment;
2741 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002742
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002743 bool startAfter(const Work& after) {
2744 fIndex = after.fIndex;
2745 return advance();
2746 }
2747
2748 SkScalar top() const {
2749 return bounds().fTop;
2750 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002751
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002752 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002753 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002754 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002755
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002756 SkScalar x() const {
2757 return bounds().fLeft;
2758 }
2759
2760 bool xFlipped() const {
2761 return x() != pts()[0].fX;
2762 }
2763
2764 SkScalar y() const {
2765 return bounds().fTop;
2766 }
2767
2768 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002769 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002770 }
2771
2772protected:
2773 Contour* fContour;
2774 SkPoint fCubic[4];
2775 int fIndex;
2776 int fLast;
2777};
2778
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002779#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002780static void debugShowLineIntersection(int pts, const Work& wt,
2781 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002782 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002783 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
2784 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
2785 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
2786 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002787 return;
2788 }
2789 SkPoint wtOutPt, wnOutPt;
2790 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
2791 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002792 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002793 __FUNCTION__,
2794 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
2795 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
2796 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002797 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002798 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002799 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002800 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
2801 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
2802 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002803 SkDebugf(" wnTs[1]=%g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002804 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002805 SkDebugf("\n");
2806}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002807#else
2808static void debugShowLineIntersection(int , const Work& ,
2809 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002810}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002811#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002812
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002813static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002814
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002815 if (test != next) {
2816 if (test->bounds().fBottom < next->bounds().fTop) {
2817 return false;
2818 }
2819 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
2820 return true;
2821 }
2822 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002823 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002824 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002825 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002826 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002827 Work wn;
2828 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002829 if (test == next && !wn.startAfter(wt)) {
2830 continue;
2831 }
2832 do {
2833 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
2834 continue;
2835 }
2836 int pts;
2837 Intersections ts;
2838 bool swap = false;
2839 switch (wt.segmentType()) {
2840 case Work::kHorizontalLine_Segment:
2841 swap = true;
2842 switch (wn.segmentType()) {
2843 case Work::kHorizontalLine_Segment:
2844 case Work::kVerticalLine_Segment:
2845 case Work::kLine_Segment: {
2846 pts = HLineIntersect(wn.pts(), wt.left(),
2847 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002848 debugShowLineIntersection(pts, wt, wn,
2849 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002850 break;
2851 }
2852 case Work::kQuad_Segment: {
2853 pts = HQuadIntersect(wn.pts(), wt.left(),
2854 wt.right(), wt.y(), wt.xFlipped(), ts);
2855 break;
2856 }
2857 case Work::kCubic_Segment: {
2858 pts = HCubicIntersect(wn.pts(), wt.left(),
2859 wt.right(), wt.y(), wt.xFlipped(), ts);
2860 break;
2861 }
2862 default:
2863 SkASSERT(0);
2864 }
2865 break;
2866 case Work::kVerticalLine_Segment:
2867 swap = true;
2868 switch (wn.segmentType()) {
2869 case Work::kHorizontalLine_Segment:
2870 case Work::kVerticalLine_Segment:
2871 case Work::kLine_Segment: {
2872 pts = VLineIntersect(wn.pts(), wt.top(),
2873 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002874 debugShowLineIntersection(pts, wt, wn,
2875 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002876 break;
2877 }
2878 case Work::kQuad_Segment: {
2879 pts = VQuadIntersect(wn.pts(), wt.top(),
2880 wt.bottom(), wt.x(), wt.yFlipped(), ts);
2881 break;
2882 }
2883 case Work::kCubic_Segment: {
2884 pts = VCubicIntersect(wn.pts(), wt.top(),
2885 wt.bottom(), wt.x(), wt.yFlipped(), ts);
2886 break;
2887 }
2888 default:
2889 SkASSERT(0);
2890 }
2891 break;
2892 case Work::kLine_Segment:
2893 switch (wn.segmentType()) {
2894 case Work::kHorizontalLine_Segment:
2895 pts = HLineIntersect(wt.pts(), wn.left(),
2896 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002897 debugShowLineIntersection(pts, wt, wn,
2898 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002899 break;
2900 case Work::kVerticalLine_Segment:
2901 pts = VLineIntersect(wt.pts(), wn.top(),
2902 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002903 debugShowLineIntersection(pts, wt, wn,
2904 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002905 break;
2906 case Work::kLine_Segment: {
2907 pts = LineIntersect(wt.pts(), wn.pts(), ts);
2908 debugShowLineIntersection(pts, wt, wn,
2909 ts.fT[1], ts.fT[0]);
2910 break;
2911 }
2912 case Work::kQuad_Segment: {
2913 swap = true;
2914 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
2915 break;
2916 }
2917 case Work::kCubic_Segment: {
2918 swap = true;
2919 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
2920 break;
2921 }
2922 default:
2923 SkASSERT(0);
2924 }
2925 break;
2926 case Work::kQuad_Segment:
2927 switch (wn.segmentType()) {
2928 case Work::kHorizontalLine_Segment:
2929 pts = HQuadIntersect(wt.pts(), wn.left(),
2930 wn.right(), wn.y(), wn.xFlipped(), ts);
2931 break;
2932 case Work::kVerticalLine_Segment:
2933 pts = VQuadIntersect(wt.pts(), wn.top(),
2934 wn.bottom(), wn.x(), wn.yFlipped(), ts);
2935 break;
2936 case Work::kLine_Segment: {
2937 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
2938 break;
2939 }
2940 case Work::kQuad_Segment: {
2941 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
2942 break;
2943 }
2944 case Work::kCubic_Segment: {
2945 wt.promoteToCubic();
2946 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
2947 break;
2948 }
2949 default:
2950 SkASSERT(0);
2951 }
2952 break;
2953 case Work::kCubic_Segment:
2954 switch (wn.segmentType()) {
2955 case Work::kHorizontalLine_Segment:
2956 pts = HCubicIntersect(wt.pts(), wn.left(),
2957 wn.right(), wn.y(), wn.xFlipped(), ts);
2958 break;
2959 case Work::kVerticalLine_Segment:
2960 pts = VCubicIntersect(wt.pts(), wn.top(),
2961 wn.bottom(), wn.x(), wn.yFlipped(), ts);
2962 break;
2963 case Work::kLine_Segment: {
2964 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
2965 break;
2966 }
2967 case Work::kQuad_Segment: {
2968 wn.promoteToCubic();
2969 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
2970 break;
2971 }
2972 case Work::kCubic_Segment: {
2973 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
2974 break;
2975 }
2976 default:
2977 SkASSERT(0);
2978 }
2979 break;
2980 default:
2981 SkASSERT(0);
2982 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002983 if (!foundCommonContour && pts > 0) {
2984 test->addCross(next);
2985 next->addCross(test);
2986 foundCommonContour = true;
2987 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002988 // in addition to recording T values, record matching segment
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002989 if (pts == 2 && wn.segmentType() <= Work::kLine_Segment
2990 && wt.segmentType() <= Work::kLine_Segment) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002991 wt.addCoincident(wn, ts, swap);
2992 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002993 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002994 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002995 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
2996 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002997 int testTAt = wt.addT(ts.fT[swap][pt], wn);
2998 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002999 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
3000 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003001 }
3002 } while (wn.advance());
3003 } while (wt.advance());
3004 return true;
3005}
3006
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003007// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003008// see if coincidence is formed by clipping non-concident segments
3009static void coincidenceCheck(SkTDArray<Contour*>& contourList, int winding) {
3010 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00003011 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003012 Contour* contour = contourList[cIndex];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003013 contour->findTooCloseToCall(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003014 }
3015 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
3016 Contour* contour = contourList[cIndex];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003017 contour->resolveCoincidence(winding);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003018 }
3019}
3020
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003021// project a ray from the top of the contour up and see if it hits anything
3022// note: when we compute line intersections, we keep track of whether
3023// two contours touch, so we need only look at contours not touching this one.
3024// OPTIMIZATION: sort contourList vertically to avoid linear walk
3025static int innerContourCheck(SkTDArray<Contour*>& contourList,
3026 Contour* baseContour, const SkPoint& basePt) {
3027 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003028 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00003029 const Segment* test = NULL;
3030 int tIndex;
3031 double tHit;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003032 for (int cTest = 0; cTest < contourCount; ++cTest) {
3033 Contour* contour = contourList[cTest];
3034 if (basePt.fY < contour->bounds().fTop) {
3035 continue;
3036 }
3037 if (bestY > contour->bounds().fBottom) {
3038 continue;
3039 }
3040 if (baseContour->crosses(contour)) {
3041 continue;
3042 }
caryclark@google.com47580692012-07-23 12:14:49 +00003043 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
3044 if (next) {
3045 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003046 }
caryclark@google.com47580692012-07-23 12:14:49 +00003047 }
3048 if (!test) {
3049 baseContour->setWinding(0);
3050 return 0;
3051 }
3052 int winding, windValue;
3053 // If the ray hit the end of a span, we need to construct the wheel of
3054 // angles to find the span closest to the ray -- even if there are just
3055 // two spokes on the wheel.
caryclark@google.come21cb182012-07-23 21:26:31 +00003056 if (fabs(tHit - test->t(tIndex)) < FLT_EPSILON) {
caryclark@google.com47580692012-07-23 12:14:49 +00003057 SkTDArray<Angle> angles;
3058 int end = test->nextSpan(tIndex, 1);
3059 if (end < 0) {
3060 end = test->nextSpan(tIndex, -1);
3061 }
3062 test->addTwoAngles(end, tIndex, angles);
3063 test->buildAngles(tIndex, angles);
3064 SkTDArray<Angle*> sorted;
3065 // OPTIMIZATION: call a sort that, if base point is the leftmost,
3066 // returns the first counterclockwise hour before 6 o'clock,
3067 // or if the base point is rightmost, returns the first clockwise
3068 // hour after 6 o'clock
3069 sortAngles(angles, sorted);
3070#if DEBUG_SORT
caryclark@google.come21cb182012-07-23 21:26:31 +00003071 sorted[0]->segment()->debugShowSort(sorted, 0, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00003072#endif
3073 // walk the sorted angle fan to find the lowest angle
3074 // above the base point. Currently, the first angle in the sorted array
3075 // is 12 noon or an earlier hour (the next counterclockwise)
3076 int count = sorted.count();
3077 int left = -1;
3078 int right = -1;
3079 for (int index = 0; index < count; ++index) {
3080 double indexDx = sorted[index]->dx();
3081 if (indexDx < 0) {
3082 left = index;
3083 } else if (indexDx > 0) {
3084 right = index;
3085 break;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003086 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003087 }
caryclark@google.com47580692012-07-23 12:14:49 +00003088 SkASSERT(left >= 0 || right >= 0);
3089 if (left < 0) {
3090 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003091 }
caryclark@google.com47580692012-07-23 12:14:49 +00003092 const Angle* angle = sorted[left];
3093 test = angle->segment();
3094 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00003095 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003096 windValue = test->windValue(angle);
3097 #if 0
caryclark@google.comcc905052012-07-25 20:59:42 +00003098 if (angle->firstBump(winding)) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003099 winding -= test->windBump(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00003100 }
3101 #endif
3102#if DEBUG_WINDING
3103 SkDebugf("%s angle winding=%d windValue=%d\n", __FUNCTION__, winding,
3104 windValue);
3105#endif
3106 } else {
3107 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00003108 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003109 windValue = test->windValue(tIndex);
3110#if DEBUG_WINDING
3111 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
3112 windValue);
3113#endif
3114 }
3115 // see if a + change in T results in a +/- change in X (compute x'(T))
3116 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
3117#if DEBUG_WINDING
3118 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
3119#endif
3120 SkASSERT(dx != 0);
3121 if (winding * dx > 0) { // if same signs, result is negative
3122 winding += dx > 0 ? -windValue : windValue;
3123#if DEBUG_WINDING
3124 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
3125#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003126 }
3127 baseContour->setWinding(winding);
3128 return winding;
3129}
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003130
3131// OPTIMIZATION: not crazy about linear search here to find top active y.
3132// seems like we should break down and do the sort, or maybe sort each
3133// contours' segments?
3134// Once the segment array is built, there's no reason I can think of not to
3135// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003136// FIXME: return the contour found to pass to inner contour check
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003137static Segment* findTopContour(SkTDArray<Contour*>& contourList,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003138 Contour*& topContour) {
3139 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003140 int cIndex = 0;
3141 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003142 SkScalar bestY = SK_ScalarMax;
3143 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003144 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003145 contour = contourList[cIndex];
3146 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003147 } while (!topStart && ++cIndex < contourCount);
3148 if (!topStart) {
3149 return NULL;
3150 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003151 topContour = contour;
3152 while (++cIndex < contourCount) {
3153 contour = contourList[cIndex];
3154 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003155 continue;
3156 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003157 SkScalar testY = SK_ScalarMax;
3158 Segment* test = contour->topSegment(testY);
3159 if (!test || bestY <= testY) {
3160 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003161 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003162 topContour = contour;
3163 topStart = test;
3164 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003165 }
3166 return topStart;
3167}
3168
caryclark@google.come21cb182012-07-23 21:26:31 +00003169static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
3170 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003171 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00003172 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003173 const Span& backPtr = span->fOther->span(span->fOtherIndex);
3174 Segment* segment = backPtr.fOther;
3175 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003176 SkTDArray<Angle> angles;
3177 int done = 0;
3178 if (segment->activeAngle(tIndex, done, angles)) {
3179 Angle* last = angles.end() - 1;
3180 tIndex = last->start();
3181 endIndex = last->end();
3182 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003183 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00003184 if (done == angles.count()) {
3185 chase.pop(&span);
3186 continue;
3187 }
3188 SkTDArray<Angle*> sorted;
3189 sortAngles(angles, sorted);
3190 // find first angle, initialize winding to computed fWindSum
3191 int firstIndex = -1;
3192 const Angle* angle;
caryclark@google.come21cb182012-07-23 21:26:31 +00003193 int spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003194 do {
3195 angle = sorted[++firstIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00003196 spanWinding = angle->segment()->windSum(angle);
3197 } while (spanWinding == SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003198 #if DEBUG_SORT
caryclark@google.come21cb182012-07-23 21:26:31 +00003199 angle->segment()->debugShowSort(sorted, firstIndex, contourWinding,
3200 spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00003201 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00003202 if (angle->firstBump(spanWinding)) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003203 spanWinding -= angle->segment()->windBump(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00003204 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00003205 // we care about first sign and whether wind sum indicates this
3206 // edge is inside or outside. Maybe need to pass span winding
3207 // or first winding or something into this function?
3208 // advance to first undone angle, then return it and winding
3209 // (to set whether edges are active or not)
3210 int nextIndex = firstIndex + 1;
3211 int angleCount = sorted.count();
3212 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
3213 do {
3214 SkASSERT(nextIndex != firstIndex);
3215 if (nextIndex == angleCount) {
3216 nextIndex = 0;
3217 }
3218 const Angle* angle = sorted[nextIndex];
3219 segment = angle->segment();
caryclark@google.come21cb182012-07-23 21:26:31 +00003220 int maxWinding = spanWinding;
caryclark@google.comafe56de2012-07-24 18:11:03 +00003221 spanWinding -= segment->windBump(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00003222 if (maxWinding * spanWinding < 0) {
3223 SkDebugf("%s flipped sign %d %d\n", __FUNCTION__, maxWinding, spanWinding);
caryclark@google.com9764cc62012-07-12 19:29:45 +00003224 }
3225 tIndex = angle->start();
3226 endIndex = angle->end();
3227 int lesser = SkMin32(tIndex, endIndex);
3228 const Span& nextSpan = segment->span(lesser);
3229 if (!nextSpan.fDone) {
3230 // FIXME: this be wrong. assign startWinding if edge is in
3231 // same direction. If the direction is opposite, winding to
3232 // assign is flipped sign or +/- 1?
caryclark@google.come21cb182012-07-23 21:26:31 +00003233 if (abs(maxWinding) < abs(spanWinding)) {
3234 maxWinding = spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003235 }
3236 segment->markWinding(lesser, maxWinding);
3237 break;
3238 }
3239 } while (++nextIndex != lastIndex);
3240 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003241 }
3242 return NULL;
3243}
3244
caryclark@google.com027de222012-07-12 12:52:50 +00003245#if DEBUG_ACTIVE_SPANS
3246static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
3247 for (int index = 0; index < contourList.count(); ++ index) {
3248 contourList[index]->debugShowActiveSpans();
3249 }
3250}
3251#endif
3252
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003253// Each segment may have an inside or an outside. Segments contained within
3254// winding may have insides on either side, and form a contour that should be
3255// ignored. Segments that are coincident with opposing direction segments may
3256// have outsides on either side, and should also disappear.
3257// 'Normal' segments will have one inside and one outside. Subsequent connections
3258// when winding should follow the intersection direction. If more than one edge
3259// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003260 // since we start with leftmost top edge, we'll traverse through a
3261 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003262static void bridge(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003263 bool firstContour = true;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003264 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003265 Contour* topContour;
3266 Segment* topStart = findTopContour(contourList, topContour);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003267 if (!topStart) {
3268 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00003269 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003270 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00003271 // follow edges to intersection by changing the index by direction.
3272 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003273 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003274 int contourWinding;
3275 if (firstContour) {
3276 contourWinding = 0;
3277 firstContour = false;
3278 } else {
3279 const SkPoint& topPoint = current->xyAtT(endIndex);
3280 contourWinding = innerContourCheck(contourList, topContour, topPoint);
3281#if DEBUG_WINDING
3282 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
3283#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003284 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003285 SkPoint lastPt;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003286 bool firstTime = true;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003287 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003288 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003289 // int firstWinding = contourWinding + spanWinding;
3290 // FIXME: needs work. While it works in limited situations, it does
3291 // not always compute winding correctly. Active should be removed and instead
3292 // the initial winding should be correctly passed in so that if the
3293 // inner contour is wound the same way, it never finds an accumulated
3294 // winding of zero. Inside 'find next', we need to look for transitions
3295 // other than zero when resolving sorted angles.
3296 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003297 do {
caryclark@google.come21cb182012-07-23 21:26:31 +00003298 bool active = winding * spanWinding <= 0
3299 && abs(winding) <= abs(spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003300 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00003301 if (abs(winding) > abs(spanWinding) && winding * spanWinding < 0) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003302 SkDebugf("%s *** unexpected active?\n", __FUNCTION__);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003303 }
caryclark@google.come21cb182012-07-23 21:26:31 +00003304 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
3305 __FUNCTION__, active ? "true" : "false",
3306 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003307 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003308 const SkPoint* firstPt = NULL;
3309 do {
3310 SkASSERT(!current->done());
caryclark@google.comafe56de2012-07-24 18:11:03 +00003311 int nextStart, nextEnd;
caryclark@google.come21cb182012-07-23 21:26:31 +00003312 Segment* next = current->findNext(chaseArray, winding,
caryclark@google.comafe56de2012-07-24 18:11:03 +00003313 contourWinding, firstTime, active, index, endIndex,
3314 nextStart, nextEnd, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003315 if (!next) {
3316 break;
3317 }
3318 if (!firstPt) {
3319 firstPt = &current->addMoveTo(index, simple, active);
3320 }
3321 lastPt = current->addCurveTo(index, endIndex, simple, active);
3322 current = next;
3323 index = nextStart;
3324 endIndex = nextEnd;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003325 firstTime = false;
3326 } while (*firstPt != lastPt && (active || !current->done()));
3327 if (firstPt && active) {
3328 #if DEBUG_PATH_CONSTRUCTION
3329 SkDebugf("%s close\n", __FUNCTION__);
3330 #endif
3331 simple.close();
3332 }
caryclark@google.come21cb182012-07-23 21:26:31 +00003333 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003334 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00003335 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003336 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003337 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003338 break;
3339 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003340 int lesser = SkMin32(index, endIndex);
3341 spanWinding = current->windSum(lesser);
3342 int spanValue = current->windValue(lesser);
3343 SkASSERT(spanWinding != SK_MinS32);
3344 int spanSign = current->spanSign(index, endIndex);
3345 #if DEBUG_WINDING
3346 SkDebugf("%s spanWinding=%d spanSign=%d winding=%d spanValue=%d\n",
3347 __FUNCTION__, spanWinding, spanSign, winding, spanValue);
3348 #endif
3349 if (spanWinding * spanSign < 0) {
3350 #if DEBUG_WINDING
3351 SkDebugf("%s spanWinding * spanSign < 0\n", __FUNCTION__);
3352 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003353 // SkTSwap<int>(index, endIndex);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003354 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003355 if (abs(spanWinding) > spanValue) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003356 winding = spanWinding;
3357 spanWinding = spanValue * SkSign32(spanWinding);
3358 winding -= spanWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00003359 #if DEBUG_WINDING
3360 SkDebugf("%s spanWinding=%d winding=%d\n", __FUNCTION__,
3361 spanWinding, winding);
3362 #endif
3363 } else {
3364 #if DEBUG_WINDING
3365 SkDebugf("%s ->0 contourWinding=%d winding=%d\n", __FUNCTION__,
3366 contourWinding, winding);
3367 #endif
3368 winding = 0;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003369 }
3370 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003371 } while (true);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003372}
3373
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003374static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
3375 int contourCount = contourList.count();
3376 for (int cTest = 0; cTest < contourCount; ++cTest) {
3377 Contour* contour = contourList[cTest];
3378 contour->fixOtherTIndex();
3379 }
3380}
3381
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003382static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003383 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003384 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003385 if (count == 0) {
3386 return;
3387 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003388 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003389 *list.append() = &contours[index];
3390 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003391 QSort<Contour>(list.begin(), list.end() - 1);
3392}
3393
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003394void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003395 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003396 int winding = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003397 simple.reset();
3398 simple.setFillType(SkPath::kEvenOdd_FillType);
3399
3400 // turn path into list of segments
3401 SkTArray<Contour> contours;
3402 // FIXME: add self-intersecting cubics' T values to segment
3403 EdgeBuilder builder(path, contours);
3404 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003405 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003406 Contour** currentPtr = contourList.begin();
3407 if (!currentPtr) {
3408 return;
3409 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003410 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003411 // find all intersections between segments
3412 do {
3413 Contour** nextPtr = currentPtr;
3414 Contour* current = *currentPtr++;
3415 Contour* next;
3416 do {
3417 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003418 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003419 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003420 // eat through coincident edges
3421 coincidenceCheck(contourList, winding);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003422 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003423 // construct closed contours
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003424 bridge(contourList, simple);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003425}
3426