blob: 5fbf4e509c808d9f37cadc369e4353c23ff95c39 [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.com200c2112012-08-03 15:05:04 +000030#if 0 // set to 1 for multiple thread -- no debugging
caryclark@google.com47580692012-07-23 12:14:49 +000031
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.com200c2112012-08-03 15:05:04 +000052#define DEBUG_ADD_T_PAIR 1
53#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000054#define DEBUG_CROSS 0
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.com534aa5b2012-08-02 20:08:21 +000064#if (DEBUG_ACTIVE_SPANS || DEBUG_CONCIDENT || DEBUG_SORT) && !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.com7db7c6b2012-07-27 21:22:25 +0000486 double dy() const {
487 return fDy;
488 }
489
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000490 int end() const {
491 return fEnd;
492 }
493
caryclark@google.comcc905052012-07-25 20:59:42 +0000494 bool firstBump(int sumWinding) const {
caryclark@google.com534aa5b2012-08-02 20:08:21 +0000495 SkDebugf("%s sign=%d sumWinding=%d\n", __FUNCTION__, sign(), sumWinding);
caryclark@google.comafe56de2012-07-24 18:11:03 +0000496 return sign() * sumWinding > 0;
497 }
498
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000499 bool isHorizontal() const {
500 return fDy == 0 && fDDy == 0 && fDDDy == 0;
501 }
502
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000503 // since all angles share a point, this needs to know which point
504 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
505 // practically, this should only be called by addAngle
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000506 void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000507 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000508 SkASSERT(start != end);
509 fSegment = segment;
510 fStart = start;
511 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000512 fDx = pts[1].fX - pts[0].fX; // b - a
513 fDy = pts[1].fY - pts[0].fY;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000514 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000515 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000516 return;
517 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000518 fDDx = pts[2].fX - pts[1].fX - fDx; // a - 2b + c
519 fDDy = pts[2].fY - pts[1].fY - fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000520 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000521 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000522 return;
523 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000524 fDDDx = pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX;
525 fDDDy = pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY;
526 }
527
528 // noncoincident quads/cubics may have the same initial angle
529 // as lines, so must sort by derivatives as well
530 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000531 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000532 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000533 fSegment = segment;
534 fStart = start;
535 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000536 fDx = pts[1].fX - pts[0].fX; // b - a
537 fDy = pts[1].fY - pts[0].fY;
538 if (verb == SkPath::kLine_Verb) {
539 fDDx = fDDy = fDDDx = fDDDy = 0;
540 return;
541 }
542 if (verb == SkPath::kQuad_Verb) {
543 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
544 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
545 int larger = std::max(abs(uplsX), abs(uplsY));
546 int shift = 0;
547 double flatT;
548 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
549 LineParameters implicitLine;
550 _Line tangent = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}};
551 implicitLine.lineEndPoints(tangent);
552 implicitLine.normalize();
553 while (larger > UlpsEpsilon * 1024) {
554 larger >>= 2;
555 ++shift;
556 flatT = 0.5 / (1 << shift);
557 QuadXYAtT(pts, flatT, &ddPt);
558 _Point _pt = {ddPt.fX, ddPt.fY};
559 double distance = implicitLine.pointDistance(_pt);
560 if (approximately_zero(distance)) {
561 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
562 break;
563 }
564 }
565 flatT = 0.5 / (1 << shift);
566 QuadXYAtT(pts, flatT, &ddPt);
567 fDDx = ddPt.fX - pts[0].fX;
568 fDDy = ddPt.fY - pts[0].fY;
569 SkASSERT(fDDx != 0 || fDDy != 0);
570 fDDDx = fDDDy = 0;
571 return;
572 }
573 SkASSERT(0); // FIXME: add cubic case
574 }
575
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000576 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000577 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000578 }
579
580 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000581 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000582 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000583
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000584 int start() const {
585 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000586 }
587
588private:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000589 SkScalar fDx;
590 SkScalar fDy;
591 SkScalar fDDx;
592 SkScalar fDDy;
593 SkScalar fDDDx;
594 SkScalar fDDDy;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000595 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000596 int fStart;
597 int fEnd;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000598};
599
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000600static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
601 int angleCount = angles.count();
602 int angleIndex;
603 angleList.setReserve(angleCount);
604 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
605 *angleList.append() = &angles[angleIndex];
606 }
607 QSort<Angle>(angleList.begin(), angleList.end() - 1);
608}
609
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000610// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000611struct Bounds : public SkRect {
612 static bool Intersects(const Bounds& a, const Bounds& b) {
613 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
614 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
615 }
616
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000617 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
618 if (left < fLeft) {
619 fLeft = left;
620 }
621 if (top < fTop) {
622 fTop = top;
623 }
624 if (right > fRight) {
625 fRight = right;
626 }
627 if (bottom > fBottom) {
628 fBottom = bottom;
629 }
630 }
631
632 void add(const Bounds& toAdd) {
633 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
634 }
635
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000636 bool isEmpty() {
637 return fLeft > fRight || fTop > fBottom
638 || fLeft == fRight && fTop == fBottom
639 || isnan(fLeft) || isnan(fRight)
640 || isnan(fTop) || isnan(fBottom);
641 }
642
643 void setCubicBounds(const SkPoint a[4]) {
644 _Rect dRect;
645 Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
646 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
647 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000648 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
649 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000650 }
651
652 void setQuadBounds(const SkPoint a[3]) {
653 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
654 {a[2].fX, a[2].fY}};
655 _Rect dRect;
656 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000657 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
658 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000659 }
660};
661
caryclark@google.com15fa1382012-05-07 20:49:36 +0000662struct Span {
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000663 Segment* fOther;
caryclark@google.com27c449a2012-07-27 18:26:38 +0000664 mutable SkPoint fPt; // lazily computed as needed
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000665 double fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000666 double fOtherT; // value at fOther[fOtherIndex].fT
667 int fOtherIndex; // can't be used during intersection
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000668 int fWindSum; // accumulated from contours surrounding this one
669 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000670 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000671};
672
673class Segment {
674public:
675 Segment() {
676#if DEBUG_DUMP
677 fID = ++gSegmentID;
678#endif
679 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000680
caryclark@google.com9764cc62012-07-12 19:29:45 +0000681 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
682 if (activeAngleInner(index, done, angles)) {
683 return true;
684 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000685 double referenceT = fTs[index].fT;
686 int lesser = index;
687 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000688 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000689 return true;
690 }
691 }
692 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000693 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000694 return true;
695 }
696 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
697 return false;
698 }
699
caryclark@google.com9764cc62012-07-12 19:29:45 +0000700 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000701 Span* span = &fTs[index];
702 Segment* other = span->fOther;
703 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +0000704 return other->activeAngleInner(oIndex, done, angles);
705 }
706
707 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
708 int next = nextSpan(index, 1);
709 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000710 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000711 if (upSpan.fWindValue) {
712 addAngle(angles, index, next);
713 if (upSpan.fDone) {
714 done++;
715 } else if (upSpan.fWindSum != SK_MinS32) {
716 return true;
717 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000718 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000719 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000720 int prev = nextSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000721 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +0000722 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000723 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000724 if (downSpan.fWindValue) {
725 addAngle(angles, index, prev);
726 if (downSpan.fDone) {
727 done++;
728 } else if (downSpan.fWindSum != SK_MinS32) {
729 return true;
730 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000731 }
732 }
733 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000734 }
735
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000736 SkScalar activeTop() const {
737 SkASSERT(!done());
738 int count = fTs.count();
739 SkScalar result = SK_ScalarMax;
740 bool lastDone = true;
741 for (int index = 0; index < count; ++index) {
742 bool done = fTs[index].fDone;
743 if (!done || !lastDone) {
744 SkScalar y = yAtT(index);
745 if (result > y) {
746 result = y;
747 }
748 }
749 lastDone = done;
750 }
751 SkASSERT(result < SK_ScalarMax);
752 return result;
753 }
754
755 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000756 SkASSERT(start != end);
757 SkPoint edge[4];
758 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
759 Angle* angle = angles.append();
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000760 angle->set(edge, fVerb, this, start, end);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000761 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000762
caryclark@google.comcc905052012-07-25 20:59:42 +0000763 void addCancelOutsides(const SkTDArray<double>& outsideTs, Segment& other,
764 double oEnd) {
765 int tIndex = -1;
766 int tCount = fTs.count();
767 int oIndex = -1;
768 int oCount = other.fTs.count();
769 double tStart = outsideTs[0];
770 double oStart = outsideTs[1];
771 do {
772 ++tIndex;
773 } while (tStart - fTs[tIndex].fT >= FLT_EPSILON && tIndex < tCount);
774 int tIndexStart = tIndex;
775 do {
776 ++oIndex;
777 } while (oStart - other.fTs[oIndex].fT >= FLT_EPSILON && oIndex < oCount);
778 int oIndexStart = oIndex;
779 double nextT;
780 do {
781 nextT = fTs[++tIndex].fT;
782 } while (nextT < 1 && nextT - tStart < FLT_EPSILON);
783 double oNextT;
784 do {
785 oNextT = other.fTs[++oIndex].fT;
786 } while (oNextT < 1 && oNextT - oStart < FLT_EPSILON);
787 // at this point, spans before and after are at:
788 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
789 // if tIndexStart == 0, no prior span
790 // if nextT == 1, no following span
791
792 // advance the span with zero winding
793 // if the following span exists (not past the end, non-zero winding)
794 // connect the two edges
795 if (!fTs[tIndexStart].fWindValue) {
796 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
797 #if DEBUG_CONCIDENT
798 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
799 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000800 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
801 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +0000802 #endif
caryclark@google.com27c449a2012-07-27 18:26:38 +0000803 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +0000804 }
805 if (nextT < 1 && fTs[tIndex].fWindValue) {
806 #if DEBUG_CONCIDENT
807 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
808 __FUNCTION__, fID, other.fID, tIndex,
809 fTs[tIndex].fT, xyAtT(tIndex).fX,
810 xyAtT(tIndex).fY);
811 #endif
812 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT);
813 }
814 } else {
815 SkASSERT(!other.fTs[oIndexStart].fWindValue);
816 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
817 #if DEBUG_CONCIDENT
818 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
819 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000820 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
821 other.xyAtT(oIndexStart).fY);
822 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +0000823 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +0000824 }
825 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
826 #if DEBUG_CONCIDENT
827 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
828 __FUNCTION__, fID, other.fID, oIndex,
829 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
830 other.xyAtT(oIndex).fY);
831 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
832 #endif
833 }
834 }
835 }
836
837 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
838 double oEnd) {
839 // walk this to outsideTs[0]
840 // walk other to outsideTs[1]
841 // if either is > 0, add a pointer to the other, copying adjacent winding
842 int tIndex = -1;
843 int oIndex = -1;
844 double tStart = outsideTs[0];
845 double oStart = outsideTs[1];
846 do {
847 ++tIndex;
848 } while (tStart - fTs[tIndex].fT >= FLT_EPSILON);
849 do {
850 ++oIndex;
851 } while (oStart - other.fTs[oIndex].fT >= FLT_EPSILON);
852 if (tIndex > 0 || oIndex > 0) {
853 addTPair(tStart, other, oStart);
854 }
855 tStart = fTs[tIndex].fT;
856 oStart = other.fTs[oIndex].fT;
857 do {
858 double nextT;
859 do {
860 nextT = fTs[++tIndex].fT;
861 } while (nextT - tStart < FLT_EPSILON);
862 tStart = nextT;
863 do {
864 nextT = other.fTs[++oIndex].fT;
865 } while (nextT - oStart < FLT_EPSILON);
866 oStart = nextT;
867 if (tStart == 1 && oStart == 1) {
868 break;
869 }
870 addTPair(tStart, other, oStart);
871 } while (tStart < 1 && oStart < 1 && oEnd - oStart >= FLT_EPSILON);
872 }
873
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000874 void addCubic(const SkPoint pts[4]) {
875 init(pts, SkPath::kCubic_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000876 fBounds.setCubicBounds(pts);
877 }
878
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000879 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000880 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000881 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000882 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000883 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000884 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000885 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000886 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
887 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
888 if (fVerb > 1) {
889 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
890 }
891 if (fVerb > 2) {
892 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
893 }
894 SkDebugf("\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000895 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000896 switch (fVerb) {
897 case SkPath::kLine_Verb:
898 path.lineTo(edge[1].fX, edge[1].fY);
899 break;
900 case SkPath::kQuad_Verb:
901 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
902 break;
903 case SkPath::kCubic_Verb:
904 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
905 edge[3].fX, edge[3].fY);
906 break;
907 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000908 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000909 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000910 }
911
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000912 void addLine(const SkPoint pts[2]) {
913 init(pts, SkPath::kLine_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000914 fBounds.set(pts, 2);
915 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000916
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000917 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
918 const SkPoint& pt = xyAtT(tIndex);
919 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000920 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000921 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000922 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000923 path.moveTo(pt.fX, pt.fY);
924 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000925 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000926 }
927
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000928 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000929 void addOtherT(int index, double otherT, int otherIndex) {
930 Span& span = fTs[index];
931 span.fOtherT = otherT;
932 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000933 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000934
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000935 void addQuad(const SkPoint pts[3]) {
936 init(pts, SkPath::kQuad_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000937 fBounds.setQuadBounds(pts);
938 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000939
940 // Defer all coincident edge processing until
941 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000942
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000943// no need to be tricky; insert in normal T order
944// resolve overlapping ts when considering coincidence later
945
946 // add non-coincident intersection. Resulting edges are sorted in T.
947 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000948 // FIXME: in the pathological case where there is a ton of intercepts,
949 // binary search?
950 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000951 size_t tCount = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000952 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000953 // OPTIMIZATION: if there are three or more identical Ts, then
954 // the fourth and following could be further insertion-sorted so
955 // that all the edges are clockwise or counterclockwise.
956 // This could later limit segment tests to the two adjacent
957 // neighbors, although it doesn't help with determining which
958 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000959 if (newT < fTs[index].fT) {
960 insertedAt = index;
961 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000962 }
963 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000964 Span* span;
965 if (insertedAt >= 0) {
966 span = fTs.insert(insertedAt);
967 } else {
968 insertedAt = tCount;
969 span = fTs.append();
970 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000971 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000972 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +0000973 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000974 span->fWindSum = SK_MinS32;
975 span->fWindValue = 1;
976 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000977 ++fDoneSpans;
978 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000979 return insertedAt;
980 }
981
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000982 // set spans from start to end to decrement by one
983 // note this walks other backwards
984 // FIMXE: there's probably an edge case that can be constructed where
985 // two span in one segment are separated by float epsilon on one span but
986 // not the other, if one segment is very small. For this
987 // case the counts asserted below may or may not be enough to separate the
988 // spans. Even if the counts work out, what if the spanw aren't correctly
989 // sorted? It feels better in such a case to match the span's other span
990 // pointer since both coincident segments must contain the same spans.
991 void addTCancel(double startT, double endT, Segment& other,
992 double oStartT, double oEndT) {
993 SkASSERT(endT - startT >= FLT_EPSILON);
994 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
995 int index = 0;
996 while (startT - fTs[index].fT >= FLT_EPSILON) {
997 ++index;
998 }
caryclark@google.comb9738012012-07-03 19:53:30 +0000999 int oIndex = other.fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001000 while (other.fTs[--oIndex].fT - oEndT > -FLT_EPSILON)
1001 ;
1002 Span* test = &fTs[index];
1003 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001004 SkTDArray<double> outsideTs;
1005 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001006 do {
1007 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001008 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001009 double testT = test->fT;
1010 double oTestT = oTest->fT;
1011 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001012 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001013 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001014 decrementSpan(span);
1015 } else if (track && span->fT < 1 && oTestT < 1) {
1016 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001017 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001018 span = &fTs[++index];
1019 } while (span->fT - testT < FLT_EPSILON);
1020 Span* oSpan = oTest;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001021 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001022 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001023 other.decrementSpan(oSpan);
1024 } else if (track && oSpan->fT < 1 && testT < 1) {
1025 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001026 }
1027 if (!oIndex) {
1028 break;
1029 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001030 oSpan = &other.fTs[--oIndex];
1031 } while (oTestT - oSpan->fT < FLT_EPSILON);
1032 test = span;
1033 oTest = oSpan;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001034 } while (test->fT < endT - FLT_EPSILON);
1035 SkASSERT(!oIndex || oTest->fT <= oStartT - FLT_EPSILON);
caryclark@google.com18063442012-07-25 12:05:18 +00001036 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001037 if (!done() && outsideTs.count()) {
1038 addCancelOutsides(outsideTs, other, oEndT);
caryclark@google.com18063442012-07-25 12:05:18 +00001039 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001040 if (!other.done() && oOutsideTs.count()) {
1041 other.addCancelOutsides(oOutsideTs, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001042 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001043 }
1044
1045 // set spans from start to end to increment the greater by one and decrement
1046 // the lesser
1047 void addTCoincident(double startT, double endT, Segment& other,
1048 double oStartT, double oEndT) {
1049 SkASSERT(endT - startT >= FLT_EPSILON);
1050 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
1051 int index = 0;
1052 while (startT - fTs[index].fT >= FLT_EPSILON) {
1053 ++index;
1054 }
1055 int oIndex = 0;
1056 while (oStartT - other.fTs[oIndex].fT >= FLT_EPSILON) {
1057 ++oIndex;
1058 }
1059 Span* test = &fTs[index];
1060 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001061 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001062 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001063 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001064 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001065 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001066 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001067 bool decrementOther = test->fWindValue >= oTest->fWindValue;
1068 Span* end = test;
1069 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001070 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001071 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001072 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001073 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001074 if (transfer) {
1075 if (decrementOther) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00001076 SkASSERT(abs(end->fWindValue) <= gDebugMaxWindValue);
caryclark@google.comb9738012012-07-03 19:53:30 +00001077 ++(end->fWindValue);
caryclark@google.com18063442012-07-25 12:05:18 +00001078 } else if (decrementSpan(end)) {
1079 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001080 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001081 } else if (oTest->fWindValue) {
1082 SkASSERT(!decrementOther);
1083 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1084 TrackOutside(xOutsideTs, end->fT, oStartT);
1085 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001086 }
1087 end = &fTs[++index];
1088 } while (end->fT - test->fT < FLT_EPSILON);
1089 Span* oEnd = oTest;
1090 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001091 if (transfer) {
caryclark@google.com18063442012-07-25 12:05:18 +00001092 if (!decrementOther) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00001093 SkASSERT(abs(oEnd->fWindValue) <= gDebugMaxWindValue);
caryclark@google.comb9738012012-07-03 19:53:30 +00001094 ++(oEnd->fWindValue);
caryclark@google.com18063442012-07-25 12:05:18 +00001095 } else if (other.decrementSpan(oEnd)) {
1096 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001097 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001098 } else if (test->fWindValue) {
1099 SkASSERT(!decrementOther);
1100 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1101 SkASSERT(0); // track for later?
1102 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001103 }
1104 oEnd = &other.fTs[++oIndex];
1105 } while (oEnd->fT - oTest->fT < FLT_EPSILON);
1106 test = end;
1107 oTest = oEnd;
1108 } while (test->fT < endT - FLT_EPSILON);
1109 SkASSERT(oTest->fT < oEndT + FLT_EPSILON);
1110 SkASSERT(oTest->fT > oEndT - FLT_EPSILON);
caryclark@google.comcc905052012-07-25 20:59:42 +00001111 if (!done()) {
1112 if (outsideTs.count()) {
1113 addCoinOutsides(outsideTs, other, oEndT);
1114 }
1115 if (xOutsideTs.count()) {
1116 addCoinOutsides(xOutsideTs, other, oEndT);
1117 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001118 }
1119 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001120 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001121 }
1122 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001123
caryclark@google.comcc905052012-07-25 20:59:42 +00001124 // FIXME: this doesn't prevent the same span from being added twice
1125 // fix in caller, assert here?
caryclark@google.com47580692012-07-23 12:14:49 +00001126 void addTPair(double t, Segment& other, double otherT) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001127 int tCount = fTs.count();
1128 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1129 const Span& span = fTs[tIndex];
1130 if (span.fT > t) {
1131 break;
1132 }
1133 if (span.fT == t && span.fOther == &other && span.fOtherT == otherT) {
1134#if DEBUG_ADD_T_PAIR
1135 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1136 __FUNCTION__, fID, t, other.fID, otherT);
1137#endif
1138 return;
1139 }
1140 }
caryclark@google.com47580692012-07-23 12:14:49 +00001141#if DEBUG_ADD_T_PAIR
1142 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1143 __FUNCTION__, fID, t, other.fID, otherT);
1144#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001145 int insertedAt = addT(t, &other);
1146 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001147 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001148 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com200c2112012-08-03 15:05:04 +00001149 int nextDoorWind = SK_MaxS32;
caryclark@google.com47580692012-07-23 12:14:49 +00001150 if (insertedAt > 0) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001151 const Span& below = fTs[insertedAt - 1];
1152 if (t - below.fT < FLT_EPSILON) {
1153 nextDoorWind = below.fWindValue;
caryclark@google.com47580692012-07-23 12:14:49 +00001154 }
1155 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001156 if (nextDoorWind == SK_MaxS32 && insertedAt < tCount) {
1157 const Span& above = fTs[insertedAt + 1];
1158 if (above.fT - t < FLT_EPSILON) {
1159 nextDoorWind = above.fWindValue;
1160 }
1161 }
1162 if (nextDoorWind != SK_MaxS32) {
1163 Span& newSpan = fTs[insertedAt];
1164 newSpan.fWindValue = nextDoorWind;
1165 if (!nextDoorWind) {
1166 newSpan.fDone = true;
1167 ++fDoneSpans;
1168 }
1169 }
1170 nextDoorWind = SK_MaxS32;
1171 int oInsertedAt = newSpan.fOtherIndex;
caryclark@google.com47580692012-07-23 12:14:49 +00001172 if (oIndex > 0) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001173 const Span& oBelow = other.fTs[oInsertedAt - 1];
1174 if (otherT - oBelow.fT < FLT_EPSILON) {
1175 nextDoorWind = oBelow.fWindValue;
1176 }
1177 }
1178 if (nextDoorWind == SK_MaxS32 && oInsertedAt + 1 < other.fTs.count()) {
1179 const Span& oAbove = other.fTs[oInsertedAt + 1];
1180 if (oAbove.fT - otherT < FLT_EPSILON) {
1181 nextDoorWind = oAbove.fWindValue;
1182 }
1183 }
1184 if (nextDoorWind != SK_MaxS32) {
1185 Span& otherSpan = other.fTs[oInsertedAt];
1186 otherSpan.fWindValue = nextDoorWind;
1187 if (!oWind) {
1188 otherSpan.fDone = true;
1189 ++(other.fDoneSpans);
caryclark@google.com47580692012-07-23 12:14:49 +00001190 }
1191 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001192 }
1193
1194 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001195 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001196 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1197 addAngle(angles, end, start);
1198 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001199 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001200 int step = SkSign32(end - start);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001201 int tIndex = nextSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001202 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001203 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001204 }
1205 }
caryclark@google.com47580692012-07-23 12:14:49 +00001206
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001207 const Bounds& bounds() const {
1208 return fBounds;
1209 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001210
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001211 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001212 double referenceT = fTs[index].fT;
1213 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001214 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001215 buildAnglesInner(lesser, angles);
1216 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001217 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001218 buildAnglesInner(index, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001219 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001220 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001221
1222 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1223 Span* span = &fTs[index];
1224 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001225 // if there is only one live crossing, and no coincidence, continue
1226 // in the same direction
1227 // if there is coincidence, the only choice may be to reverse direction
1228 // find edge on either side of intersection
1229 int oIndex = span->fOtherIndex;
1230 // if done == -1, prior span has already been processed
1231 int step = 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001232 int next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001233 if (next < 0) {
1234 step = -step;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001235 next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001236 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001237 // add candidate into and away from junction
1238 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001239 }
1240
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001241 bool cancels(const Segment& other) const {
caryclark@google.comb9738012012-07-03 19:53:30 +00001242 SkASSERT(fVerb == SkPath::kLine_Verb);
1243 SkASSERT(other.fVerb == SkPath::kLine_Verb);
1244 SkPoint dxy = fPts[0] - fPts[1];
1245 SkPoint odxy = other.fPts[0] - other.fPts[1];
1246 return dxy.fX * odxy.fX < 0 || dxy.fY * odxy.fY < 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001247 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001248
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001249 // figure out if the segment's ascending T goes clockwise or not
1250 // not enough context to write this as shown
1251 // instead, add all segments meeting at the top
1252 // sort them using buildAngleList
1253 // find the first in the sort
1254 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001255 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001256 SkASSERT(0); // incomplete
1257 return false;
1258 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001259
1260 int computeSum(int startIndex, int endIndex) {
1261 SkTDArray<Angle> angles;
1262 addTwoAngles(startIndex, endIndex, angles);
1263 buildAngles(endIndex, angles);
1264 SkTDArray<Angle*> sorted;
1265 sortAngles(angles, sorted);
1266 int angleCount = angles.count();
1267 const Angle* angle;
1268 const Segment* base;
1269 int winding;
1270 int firstIndex = 0;
1271 do {
1272 angle = sorted[firstIndex];
1273 base = angle->segment();
1274 winding = base->windSum(angle);
1275 if (winding != SK_MinS32) {
1276 break;
1277 }
1278 if (++firstIndex == angleCount) {
1279 return SK_MinS32;
1280 }
1281 } while (true);
1282 // turn winding into contourWinding
1283 int spanWinding = base->spanSign(angle->start(), angle->end());
1284 if (spanWinding * winding < 0) {
1285 winding += spanWinding;
1286 }
1287 #if DEBUG_SORT
1288 base->debugShowSort(sorted, firstIndex, winding);
1289 #endif
1290 int nextIndex = firstIndex + 1;
1291 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1292 winding -= base->windBump(angle);
1293 do {
1294 if (nextIndex == angleCount) {
1295 nextIndex = 0;
1296 }
1297 angle = sorted[nextIndex];
1298 Segment* segment = angle->segment();
1299 int maxWinding = winding;
1300 winding -= segment->windBump(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001301 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001302 if (abs(maxWinding) < abs(winding) || maxWinding * winding < 0) {
1303 maxWinding = winding;
1304 }
1305 segment->markAndChaseWinding(angle, maxWinding);
1306 }
1307 } while (++nextIndex != lastIndex);
1308 return windSum(SkMin32(startIndex, endIndex));
1309 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001310
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001311 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001312 int bestT = -1;
1313 SkScalar top = bounds().fTop;
1314 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001315 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001316 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001317 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001318 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001319 if (fTs[start].fWindValue == 0) {
1320 continue;
1321 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001322 SkPoint edge[4];
1323 // OPTIMIZE: wrap this so that if start==0 end==fTCount-1 we can
1324 // work with the original data directly
1325 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001326 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001327 Intersections intersections;
1328 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1329 false, intersections);
1330 if (pts == 0) {
1331 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001332 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001333 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1334 // if the intersection is edge on, wait for another one
1335 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001336 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001337 SkASSERT(pts == 1); // FIXME: more code required to disambiguate
1338 SkPoint pt;
1339 double foundT = intersections.fT[0][0];
1340 (*SegmentXYAtT[fVerb])(fPts, foundT, &pt);
1341 if (bestY < pt.fY) {
1342 bestY = pt.fY;
1343 bestT = foundT < 1 ? start : end;
caryclark@google.com47580692012-07-23 12:14:49 +00001344 hitT = fTs[start].fT + (fTs[end].fT - fTs[start].fT) * foundT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001345 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001346 } while (fTs[end].fT != 1);
1347 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001348 }
caryclark@google.com18063442012-07-25 12:05:18 +00001349
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001350 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1351 // if a segment is connected to this one, consider it crossing
1352 int tIndex;
1353 if (fPts[0].fX == basePt.fX) {
1354 tIndex = 0;
1355 do {
1356 const Span& sSpan = fTs[tIndex];
1357 const Segment* sOther = sSpan.fOther;
1358 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1359 continue;
1360 }
1361 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1362 : sOther->fBounds.fRight > basePt.fX) {
1363 return true;
1364 }
1365 } while (fTs[++tIndex].fT == 0);
1366 }
1367 if (fPts[fVerb].fX == basePt.fX) {
1368 tIndex = fTs.count() - 1;
1369 do {
1370 const Span& eSpan = fTs[tIndex];
1371 const Segment* eOther = eSpan.fOther;
1372 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1373 continue;
1374 }
1375 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1376 : eOther->fBounds.fRight > basePt.fX) {
1377 return true;
1378 }
1379 } while (fTs[--tIndex].fT == 1);
1380 }
1381 return false;
1382 }
1383
caryclark@google.com18063442012-07-25 12:05:18 +00001384 bool decrementSpan(Span* span) {
1385 SkASSERT(span->fWindValue > 0);
1386 if (--(span->fWindValue) == 0) {
1387 span->fDone = true;
1388 ++fDoneSpans;
1389 return true;
1390 }
1391 return false;
1392 }
1393
caryclark@google.com15fa1382012-05-07 20:49:36 +00001394 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001395 SkASSERT(fDoneSpans <= fTs.count());
1396 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001397 }
1398
caryclark@google.com47580692012-07-23 12:14:49 +00001399 bool done(const Angle& angle) const {
1400 int start = angle.start();
1401 int end = angle.end();
1402 const Span& mSpan = fTs[SkMin32(start, end)];
1403 return mSpan.fDone;
1404 }
1405
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001406 // so the span needs to contain the pairing info found here
1407 // this should include the winding computed for the edge, and
1408 // what edge it connects to, and whether it is discarded
1409 // (maybe discarded == abs(winding) > 1) ?
1410 // only need derivatives for duration of sorting, add a new struct
1411 // for pairings, remove extra spans that have zero length and
1412 // reference an unused other
1413 // for coincident, the last span on the other may be marked done
1414 // (always?)
1415
1416 // if loop is exhausted, contour may be closed.
1417 // FIXME: pass in close point so we can check for closure
1418
1419 // given a segment, and a sense of where 'inside' is, return the next
1420 // segment. If this segment has an intersection, or ends in multiple
1421 // segments, find the mate that continues the outside.
1422 // note that if there are multiples, but no coincidence, we can limit
1423 // choices to connections in the correct direction
1424
1425 // mark found segments as done
1426
caryclark@google.com15fa1382012-05-07 20:49:36 +00001427 // start is the index of the beginning T of this edge
1428 // it is guaranteed to have an end which describes a non-zero length (?)
1429 // winding -1 means ccw, 1 means cw
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001430 // firstFind allows coincident edges to be treated differently
caryclark@google.com27c449a2012-07-27 18:26:38 +00001431 Segment* findNext(SkTDArray<Span*>& chase, bool firstFind, bool active,
caryclark@google.com0e08a192012-07-13 21:07:52 +00001432 const int startIndex, const int endIndex, int& nextStart,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001433 int& nextEnd, int& winding, int& spanWinding) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001434 int outerWinding = winding;
1435 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00001436 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001437 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1438 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00001439 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001440 if (abs(outerWinding) < abs(innerWinding)
1441 || outerWinding * innerWinding < 0) {
1442 outerWinding = innerWinding;
1443 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001444 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001445 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001446 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1447 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001448 int step = SkSign32(endIndex - startIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001449 int end = nextSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001450 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001451 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001452 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001453 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001454 // mark the smaller of startIndex, endIndex done, and all adjacent
1455 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001456 #if DEBUG_WINDING
1457 SkDebugf("%s simple\n", __FUNCTION__);
1458 #endif
1459 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001460 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001461 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00001462 double startT = other->fTs[nextStart].fT;
1463 nextEnd = nextStart;
1464 do {
1465 nextEnd += step;
1466 } while (fabs(startT - other->fTs[nextEnd].fT) < FLT_EPSILON);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001467 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001468 return other;
1469 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001470 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001471 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001472 SkASSERT(startIndex - endIndex != 0);
1473 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001474 addTwoAngles(startIndex, end, angles);
1475 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001476 SkTDArray<Angle*> sorted;
1477 sortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001478 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001479 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001480 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00001481 #if DEBUG_SORT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001482 debugShowSort(sorted, firstIndex, winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001483 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001484 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001485 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001486 SkDebugf("%s sign=%d\n", __FUNCTION__, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00001487 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001488 int sumWinding = winding - windBump(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001489 int nextIndex = firstIndex + 1;
1490 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1491 const Angle* foundAngle = NULL;
caryclark@google.com47580692012-07-23 12:14:49 +00001492 bool foundDone = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001493 // iterate through the angle, and compute everyone's winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001494 int toggleWinding = SK_MinS32;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001495 bool flipFound = false;
1496 int flipped = 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001497 Segment* nextSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001498 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001499 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001500 nextIndex = 0;
1501 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001502 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00001503 int maxWinding = sumWinding;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001504 nextSegment = nextAngle->segment();
1505 sumWinding -= nextSegment->windBump(nextAngle);
caryclark@google.come21cb182012-07-23 21:26:31 +00001506 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001507 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00001508 SkDebugf("%s maxWinding=%d sumWinding=%d sign=%d\n", __FUNCTION__,
1509 maxWinding, sumWinding, nextAngle->sign());
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001510 #endif
caryclark@google.come21cb182012-07-23 21:26:31 +00001511 if (maxWinding * sumWinding < 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00001512 flipFound ^= true;
caryclark@google.com47580692012-07-23 12:14:49 +00001513 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001514 SkDebugf("%s flipFound=%d maxWinding=%d sumWinding=%d\n",
1515 __FUNCTION__, flipFound, maxWinding, sumWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00001516 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001517 }
caryclark@google.come21cb182012-07-23 21:26:31 +00001518 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00001519 if (!active) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001520 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00001521 nextSegment->markWinding(SkMin32(nextAngle->start(),
1522 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001523 #if DEBUG_WINDING
caryclark@google.com5c286d32012-07-13 11:57:28 +00001524 SkDebugf("%s inactive\n", __FUNCTION__);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001525 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00001526 return NULL;
1527 }
caryclark@google.com47580692012-07-23 12:14:49 +00001528 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001529 foundAngle = nextAngle;
caryclark@google.com47580692012-07-23 12:14:49 +00001530 foundDone = nextSegment->done(*nextAngle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001531 if (flipFound || (maxWinding * outerWinding < 0)) {
caryclark@google.com47580692012-07-23 12:14:49 +00001532 flipped = -flipped;
1533 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001534 SkDebugf("%s flipped=%d flipFound=%d maxWinding=%d"
1535 " outerWinding=%d\n", __FUNCTION__, flipped,
1536 flipFound, maxWinding, outerWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00001537 #endif
1538 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001539 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001540 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001541 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001542 if (!maxWinding && !foundAngle) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00001543 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001544 if (flipped > 0) {
1545 SkDebugf("%s sumWinding=%d * outerWinding=%d < 0 (%s)\n",
1546 __FUNCTION__, sumWinding, outerWinding,
1547 sumWinding * outerWinding < 0 ? "true" : "false");
1548 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00001549 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001550 if (sumWinding * outerWinding < 0 && flipped > 0) {
1551 #if DEBUG_WINDING
1552 SkDebugf("%s toggleWinding=%d\n", __FUNCTION__, sumWinding);
1553 #endif
1554 toggleWinding = sumWinding;
1555 } else if (outerWinding != sumWinding) {
1556 #if DEBUG_WINDING
1557 SkDebugf("%s outerWinding=%d != sumWinding=%d winding=%d\n",
1558 __FUNCTION__, outerWinding, sumWinding, winding);
1559 #endif
caryclark@google.com27c449a2012-07-27 18:26:38 +00001560 winding = sumWinding;
caryclark@google.comcc905052012-07-25 20:59:42 +00001561 }
caryclark@google.com0e08a192012-07-13 21:07:52 +00001562 foundAngle = nextAngle;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001563 if (flipFound) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001564 flipped = -flipped;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001565 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001566 SkDebugf("%s flipped flipFound=%d\n", __FUNCTION__, flipFound);
caryclark@google.com27c449a2012-07-27 18:26:38 +00001567 #endif
1568 }
caryclark@google.com0e08a192012-07-13 21:07:52 +00001569 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001570 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001571 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001572 }
1573 // if the winding is non-zero, nextAngle does not connect to
1574 // current chain. If we haven't done so already, mark the angle
1575 // as done, record the winding value, and mark connected unambiguous
1576 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001577 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001578 if (abs(maxWinding) < abs(sumWinding)
1579 || maxWinding * sumWinding < 0) {
caryclark@google.come21cb182012-07-23 21:26:31 +00001580 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001581 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001582 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001583 if (foundAngle) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001584 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001585 } else {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001586 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
1587 }
1588 if (last) {
1589 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001590 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001591 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001592 } while (++nextIndex != lastIndex);
caryclark@google.com47580692012-07-23 12:14:49 +00001593 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001594 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001595 if (!foundAngle) {
1596 return NULL;
1597 }
1598 nextStart = foundAngle->start();
1599 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00001600 nextSegment = foundAngle->segment();
1601 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
1602 SkMin32(nextStart, nextEnd));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001603 if (toggleWinding != SK_MinS32) {
1604 winding = toggleWinding;
1605 spanWinding = -spanWinding;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001606 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00001607 #if DEBUG_WINDING
1608 SkDebugf("%s spanWinding=%d\n", __FUNCTION__, spanWinding);
1609 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00001610 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001611 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001612
1613 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
1614 int angleCount = sorted.count();
1615 int firstIndex = -1;
1616 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
1617 const Angle* angle = sorted[angleIndex];
1618 if (angle->segment() == this && angle->start() == end &&
1619 angle->end() == start) {
1620 firstIndex = angleIndex;
1621 break;
1622 }
1623 }
1624 return firstIndex;
1625 }
1626
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001627 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001628 void findTooCloseToCall(int /* winding */ ) { // FIXME: winding should be considered
caryclark@google.com15fa1382012-05-07 20:49:36 +00001629 int count = fTs.count();
1630 if (count < 3) { // require t=0, x, 1 at minimum
1631 return;
1632 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001633 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001634 int moCount;
1635 Span* match;
1636 Segment* mOther;
1637 do {
1638 match = &fTs[matchIndex];
1639 mOther = match->fOther;
1640 moCount = mOther->fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001641 if (moCount >= 3) {
1642 break;
1643 }
1644 if (++matchIndex >= count) {
1645 return;
1646 }
1647 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00001648 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001649 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001650 // look for a pair of nearby T values that map to the same (x,y) value
1651 // if found, see if the pair of other segments share a common point. If
1652 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00001653 for (int index = matchIndex + 1; index < count; ++index) {
1654 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001655 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001656 continue;
1657 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001658 Segment* tOther = test->fOther;
1659 int toCount = tOther->fTs.count();
1660 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001661 continue;
1662 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001663 const SkPoint* testPt = &xyAtT(test);
1664 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001665 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001666 moCount = toCount;
1667 match = test;
1668 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001669 matchPt = testPt;
1670 continue;
1671 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001672 int moStart = -1;
1673 int moEnd = -1;
1674 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001675 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001676 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001677 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001678 continue;
1679 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001680 if (moSpan.fOther == this) {
1681 if (moSpan.fOtherT == match->fT) {
1682 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001683 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001684 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001685 continue;
1686 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001687 if (moSpan.fOther == tOther) {
1688 SkASSERT(moEnd == -1);
1689 moEnd = moIndex;
1690 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001691 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001692 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001693 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001694 continue;
1695 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001696 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
1697 if (moStartT == moEndT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001698 continue;
1699 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001700 int toStart = -1;
1701 int toEnd = -1;
1702 double toStartT, toEndT;
1703 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
1704 Span& toSpan = tOther->fTs[toIndex];
1705 if (toSpan.fOther == this) {
1706 if (toSpan.fOtherT == test->fT) {
1707 toStart = toIndex;
1708 toStartT = toSpan.fT;
1709 }
1710 continue;
1711 }
1712 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
1713 SkASSERT(toEnd == -1);
1714 toEnd = toIndex;
1715 toEndT = toSpan.fT;
1716 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001717 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001718 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
1719 if (toStart <= 0 || toEnd <= 0) {
1720 continue;
1721 }
1722 if (toStartT == toEndT) {
1723 continue;
1724 }
1725 // test to see if the segment between there and here is linear
1726 if (!mOther->isLinear(moStart, moEnd)
1727 || !tOther->isLinear(toStart, toEnd)) {
1728 continue;
1729 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001730 // FIXME: defer implementation until the rest works
1731 // this may share code with regular coincident detection
1732 SkASSERT(0);
1733 #if 0
1734 if (flipped) {
1735 mOther->addTCancel(moStart, moEnd, tOther, tStart, tEnd);
1736 } else {
1737 mOther->addTCoincident(moStart, moEnd, tOther, tStart, tEnd);
1738 }
1739 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001740 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001741 }
1742
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001743 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
1744 // and use more concise logic like the old edge walker code?
1745 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001746 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001747 // iterate through T intersections and return topmost
1748 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001749 SkASSERT(!done());
1750 int firstT;
1751 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001752 SkPoint topPt;
1753 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001754 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001755 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001756 bool lastDone = true;
1757 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001758 const Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001759 if (!span.fDone || !lastDone) {
1760 const SkPoint& intercept = xyAtT(&span);
1761 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
1762 && topPt.fX > intercept.fX)) {
1763 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001764 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001765 } else if (topPt == intercept) {
1766 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001767 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001768 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001769 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001770 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001771 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001772 int step = 1;
1773 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001774 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001775 step = -1;
1776 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001777 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001778 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001779 // if the topmost T is not on end, or is three-way or more, find left
1780 // look for left-ness from tLeft to firstT (matching y of other)
1781 SkTDArray<Angle> angles;
1782 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001783 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001784 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001785 SkTDArray<Angle*> sorted;
1786 sortAngles(angles, sorted);
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001787 // skip edges that have already been processed
1788 firstT = -1;
1789 Segment* leftSegment;
1790 do {
1791 const Angle* angle = sorted[++firstT];
1792 leftSegment = angle->segment();
1793 tIndex = angle->end();
1794 endIndex = angle->start();
1795 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001796 return leftSegment;
1797 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001798
1799 bool firstBump(const Angle* angle, int sumWinding) const {
1800 int winding = spanSign(angle->start(), angle->end());
1801 sumWinding -= winding;
1802 if (sumWinding == 0) {
1803 sumWinding = winding;
1804 }
1805 bool result = angle->sign() * sumWinding > 0;
1806 SkASSERT(result == angle->firstBump(sumWinding));
1807 return result;
1808 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001809
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001810 // FIXME: not crazy about this
1811 // when the intersections are performed, the other index is into an
1812 // incomplete array. as the array grows, the indices become incorrect
1813 // while the following fixes the indices up again, it isn't smart about
1814 // skipping segments whose indices are already correct
1815 // assuming we leave the code that wrote the index in the first place
1816 void fixOtherTIndex() {
1817 int iCount = fTs.count();
1818 for (int i = 0; i < iCount; ++i) {
1819 Span& iSpan = fTs[i];
1820 double oT = iSpan.fOtherT;
1821 Segment* other = iSpan.fOther;
1822 int oCount = other->fTs.count();
1823 for (int o = 0; o < oCount; ++o) {
1824 Span& oSpan = other->fTs[o];
1825 if (oT == oSpan.fT && this == oSpan.fOther) {
1826 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001827 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001828 }
1829 }
1830 }
1831 }
1832
caryclark@google.com495f8e42012-05-31 13:13:11 +00001833 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001834 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001835 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001836 SkASSERT(end >= 0);
1837 if (multipleSpans(end)) {
1838 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001839 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001840 const Span& endSpan = fTs[end];
1841 Segment* other = endSpan.fOther;
1842 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001843 int otherEnd = other->nextSpan(index, step);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001844 Span* last = other->innerChaseDone(index, step, winding);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001845 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001846 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001847 }
1848
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001849 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001850 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001851 SkASSERT(end >= 0);
1852 if (multipleSpans(end)) {
1853 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001854 }
1855 const Span& endSpan = fTs[end];
1856 Segment* other = endSpan.fOther;
1857 index = endSpan.fOtherIndex;
1858 int otherEnd = other->nextSpan(index, step);
1859 int min = SkMin32(index, otherEnd);
1860 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00001861 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001862 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001863 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001864 Span* last = other->innerChaseWinding(index, step, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001865 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001866 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001867 }
1868
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001869 void init(const SkPoint pts[], SkPath::Verb verb) {
1870 fPts = pts;
1871 fVerb = verb;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001872 fDoneSpans = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001873 }
1874
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001875 bool intersected() const {
1876 return fTs.count() > 0;
1877 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00001878
1879 bool isConnected(int startIndex, int endIndex) const {
1880 return fTs[startIndex].fWindSum != SK_MinS32
1881 || fTs[endIndex].fWindSum != SK_MinS32;
1882 }
1883
caryclark@google.com15fa1382012-05-07 20:49:36 +00001884 bool isLinear(int start, int end) const {
1885 if (fVerb == SkPath::kLine_Verb) {
1886 return true;
1887 }
1888 if (fVerb == SkPath::kQuad_Verb) {
1889 SkPoint qPart[3];
1890 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
1891 return QuadIsLinear(qPart);
1892 } else {
1893 SkASSERT(fVerb == SkPath::kCubic_Verb);
1894 SkPoint cPart[4];
1895 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
1896 return CubicIsLinear(cPart);
1897 }
1898 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001899
1900 // OPTIMIZE: successive calls could start were the last leaves off
1901 // or calls could specialize to walk forwards or backwards
1902 bool isMissing(double startT) const {
1903 size_t tCount = fTs.count();
1904 for (size_t index = 0; index < tCount; ++index) {
1905 if (fabs(startT - fTs[index].fT) < FLT_EPSILON) {
1906 return false;
1907 }
1908 }
1909 return true;
1910 }
1911
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001912 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001913 int count = fTs.count();
1914 if (count == 2) {
1915 return true;
1916 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001917 double t = fTs[end].fT;
1918 if (t < FLT_EPSILON) {
1919 return fTs[1].fT >= FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001920 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001921 if (t > 1 - FLT_EPSILON) {
1922 return fTs[count - 2].fT <= 1 - FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001923 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001924 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001925 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001926
1927 bool isHorizontal() const {
1928 return fBounds.fTop == fBounds.fBottom;
1929 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001930
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001931 bool isVertical() const {
1932 return fBounds.fLeft == fBounds.fRight;
1933 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001934
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001935 SkScalar leftMost(int start, int end) const {
1936 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
1937 }
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001938
caryclark@google.com495f8e42012-05-31 13:13:11 +00001939 // this span is excluded by the winding rule -- chase the ends
1940 // as long as they are unambiguous to mark connections as done
1941 // and give them the same winding value
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001942 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001943 int index = angle->start();
1944 int endIndex = angle->end();
1945 int step = SkSign32(endIndex - index);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001946 Span* last = innerChaseDone(index, step, winding);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001947 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001948 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001949 }
1950
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001951 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001952 int index = angle->start();
1953 int endIndex = angle->end();
1954 int min = SkMin32(index, endIndex);
1955 int step = SkSign32(endIndex - index);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001956 Span* last = innerChaseWinding(index, step, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001957 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001958 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001959 }
1960
caryclark@google.com495f8e42012-05-31 13:13:11 +00001961 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001962 // This may be called when the segment is already marked done. While this
1963 // wastes time, it shouldn't do any more than spin through the T spans.
1964 // OPTIMIZATION: abort on first done found (assuming that this code is
1965 // always called to mark segments done).
caryclark@google.com495f8e42012-05-31 13:13:11 +00001966 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001967 // SkASSERT(!done());
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001968 double referenceT = fTs[index].fT;
1969 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001970 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001971 Span& span = fTs[lesser];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001972 if (span.fDone) {
1973 continue;
1974 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001975 #if DEBUG_MARK_DONE
1976 const SkPoint& pt = xyAtT(&span);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001977 SkDebugf("%s id=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001978 __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
1979 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001980 span.fDone = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001981 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001982 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001983 span.fWindSum = winding;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001984 fDoneSpans++;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001985 }
1986 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001987 Span& span = fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001988 // SkASSERT(!span.fDone);
1989 if (span.fDone) {
1990 continue;
1991 }
1992 #if DEBUG_MARK_DONE
1993 const SkPoint& pt = xyAtT(&span);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001994 SkDebugf("%s id=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001995 __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
1996 #endif
1997 span.fDone = true;
1998 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001999 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002000 span.fWindSum = winding;
2001 fDoneSpans++;
2002 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
2003 }
2004
2005 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002006 // SkASSERT(!done());
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002007 double referenceT = fTs[index].fT;
2008 int lesser = index;
2009 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
2010 Span& span = fTs[lesser];
2011 if (span.fDone) {
2012 continue;
2013 }
caryclark@google.com47580692012-07-23 12:14:49 +00002014 // SkASSERT(span.fWindValue == 1 || winding == 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002015 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
2016 #if DEBUG_MARK_DONE
2017 const SkPoint& pt = xyAtT(&span);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002018 SkDebugf("%s id=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002019 __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
2020 #endif
caryclark@google.com47580692012-07-23 12:14:49 +00002021 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002022 span.fWindSum = winding;
2023 }
2024 do {
2025 Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002026 // SkASSERT(!span.fDone || span.fCoincident);
2027 if (span.fDone) {
2028 continue;
2029 }
caryclark@google.com47580692012-07-23 12:14:49 +00002030 // SkASSERT(span.fWindValue == 1 || winding == 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002031 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
2032 #if DEBUG_MARK_DONE
2033 const SkPoint& pt = xyAtT(&span);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002034 SkDebugf("%s id=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002035 __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
2036 #endif
caryclark@google.com47580692012-07-23 12:14:49 +00002037 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002038 span.fWindSum = winding;
2039 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002040 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002041
caryclark@google.com9764cc62012-07-12 19:29:45 +00002042 // return span if when chasing, two or more radiating spans are not done
2043 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2044 // candidate and the remaining spans have windValue == 0 (canceled by
2045 // coincidence). The coincident edges could either be removed altogether,
2046 // or this code could be more complicated in detecting this case. Worth it?
2047 bool multipleSpans(int end) const {
2048 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002049 }
2050
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002051 // This has callers for two different situations: one establishes the end
2052 // of the current span, and one establishes the beginning of the next span
2053 // (thus the name). When this is looking for the end of the current span,
2054 // coincidence is found when the beginning Ts contain -step and the end
2055 // contains step. When it is looking for the beginning of the next, the
2056 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002057 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002058 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002059 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002060 int count = fTs.count();
2061 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002062 while (step > 0 ? ++to < count : --to >= 0) {
2063 const Span& span = fTs[to];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002064 if ((step > 0 ? span.fT - fromSpan.fT : fromSpan.fT - span.fT) < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002065 continue;
2066 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002067 return to;
2068 }
2069 return -1;
2070 }
2071
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002072 const SkPoint* pts() const {
2073 return fPts;
2074 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002075
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002076 void reset() {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002077 init(NULL, (SkPath::Verb) -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002078 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2079 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002080 }
2081
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002082 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002083 const Span& span(int tIndex) const {
2084 return fTs[tIndex];
2085 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002086
2087 int spanSign(int startIndex, int endIndex) const {
2088 return startIndex < endIndex ? -fTs[startIndex].fWindValue :
2089 fTs[endIndex].fWindValue;
2090 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002091
2092 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002093 double t(int tIndex) const {
2094 return fTs[tIndex].fT;
2095 }
caryclark@google.com18063442012-07-25 12:05:18 +00002096
2097 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
2098 double start) {
2099 int outCount = outsideTs.count();
2100 if (outCount == 0 || end - outsideTs[outCount - 2] >= FLT_EPSILON) {
2101 *outsideTs.append() = end;
2102 *outsideTs.append() = start;
2103 }
2104 }
2105
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002106 void updatePts(const SkPoint pts[]) {
2107 fPts = pts;
2108 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002109
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002110 SkPath::Verb verb() const {
2111 return fVerb;
2112 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002113
caryclark@google.comafe56de2012-07-24 18:11:03 +00002114 int windBump(const Angle* angle) const {
2115 SkASSERT(angle->segment() == this);
2116 const Span& span = fTs[SkMin32(angle->start(), angle->end())];
2117 int result = angle->sign() * span.fWindValue;
2118#if DEBUG_WIND_BUMP
2119 SkDebugf("%s bump=%d\n", __FUNCTION__, result);
2120#endif
2121 return result;
2122 }
2123
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002124 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002125 return fTs[tIndex].fWindSum;
2126 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002127
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002128 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002129 int start = angle->start();
2130 int end = angle->end();
2131 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002132 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002133 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002134
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002135 int windValue(int tIndex) const {
2136 return fTs[tIndex].fWindValue;
2137 }
2138
2139 int windValue(const Angle* angle) const {
2140 int start = angle->start();
2141 int end = angle->end();
2142 int index = SkMin32(start, end);
2143 return windValue(index);
2144 }
2145
2146 SkScalar xAtT(const Span* span) const {
2147 return xyAtT(span).fX;
2148 }
2149
2150 const SkPoint& xyAtT(int index) const {
2151 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002152 }
2153
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002154 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002155 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002156 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002157 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002158 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002159 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002160 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002161 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002162 }
2163 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002164 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002165 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002166
2167 SkScalar yAtT(int index) const {
2168 return yAtT(&fTs[index]);
2169 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002170
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002171 SkScalar yAtT(const Span* span) const {
2172 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002173 }
2174
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002175#if DEBUG_DUMP
2176 void dump() const {
2177 const char className[] = "Segment";
2178 const int tab = 4;
2179 for (int i = 0; i < fTs.count(); ++i) {
2180 SkPoint out;
2181 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
2182 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002183 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002184 tab + sizeof(className), className, fID,
2185 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002186 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002187 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002188 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002189 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00002190 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002191 }
2192#endif
2193
caryclark@google.com47580692012-07-23 12:14:49 +00002194#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00002195 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002196 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00002197 for (int i = 0; i < fTs.count(); ++i) {
2198 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
2199 return;
2200 }
2201 }
2202 SkASSERT(0);
2203 }
2204#endif
2205
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002206#if DEBUG_DUMP
2207 int debugID() const {
2208 return fID;
2209 }
2210#endif
2211
caryclark@google.comcc905052012-07-25 20:59:42 +00002212#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002213 void debugShowTs() const {
caryclark@google.com47580692012-07-23 12:14:49 +00002214 SkDebugf("%s %d", __FUNCTION__, fID);
2215 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00002216 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00002217 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
2218 }
2219 SkDebugf("\n");
2220 }
2221#endif
2222
caryclark@google.com027de222012-07-12 12:52:50 +00002223#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002224 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00002225 if (done()) {
2226 return;
2227 }
2228 for (int i = 0; i < fTs.count(); ++i) {
2229 if (fTs[i].fDone) {
2230 continue;
2231 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002232 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00002233 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2234 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2235 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2236 }
2237 const Span* span = &fTs[i];
2238 SkDebugf(") fT=%d (%1.9g) (%1.9g,%1.9g)", i, fTs[i].fT,
2239 xAtT(span), yAtT(i));
2240 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002241 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
2242 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
2243 if (fTs[i].fWindSum == SK_MinS32) {
2244 SkDebugf("?");
2245 } else {
2246 SkDebugf("%d", fTs[i].fWindSum);
2247 }
2248 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00002249 }
2250 }
2251#endif
2252
caryclark@google.com47580692012-07-23 12:14:49 +00002253#if DEBUG_SORT
caryclark@google.come21cb182012-07-23 21:26:31 +00002254 void debugShowSort(const SkTDArray<Angle*>& angles, int first,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002255 const int contourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002256 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00002257 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002258 int lastSum = contourWinding;
2259 int windSum = lastSum - windBump(angles[first]);
2260 SkDebugf("%s contourWinding=%d bump=%d\n", __FUNCTION__,
2261 contourWinding, windBump(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00002262 int index = first;
2263 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00002264 do {
2265 const Angle& angle = *angles[index];
2266 const Segment& segment = *angle.segment();
2267 int start = angle.start();
2268 int end = angle.end();
2269 const Span& sSpan = segment.fTs[start];
2270 const Span& eSpan = segment.fTs[end];
2271 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002272 if (!firstTime) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002273 lastSum = windSum;
2274 windSum -= segment.windBump(&angle);
2275 }
caryclark@google.com47580692012-07-23 12:14:49 +00002276 SkDebugf("%s [%d] id=%d start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
2277 " sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
2278 __FUNCTION__, index, segment.fID, start, segment.xAtT(&sSpan),
2279 segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
2280 segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
2281 lastSum, windSum, abs(lastSum) > abs(windSum) ? lastSum :
2282 windSum, mSpan.fDone);
2283 ++index;
2284 if (index == angles.count()) {
2285 index = 0;
2286 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002287 if (firstTime) {
2288 firstTime = false;
2289 }
caryclark@google.com47580692012-07-23 12:14:49 +00002290 } while (index != first);
2291 }
2292#endif
2293
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002294#if DEBUG_WINDING
2295 bool debugVerifyWinding(int start, int end, int winding) const {
2296 const Span& span = fTs[SkMin32(start, end)];
2297 int spanWinding = span.fWindSum;
2298 if (spanWinding == SK_MinS32) {
2299 return true;
2300 }
2301 int spanSign = SkSign32(start - end);
2302 int signedVal = spanSign * span.fWindValue;
2303 if (signedVal < 0) {
2304 spanWinding -= signedVal;
2305 }
2306 return span.fWindSum == winding;
2307 }
2308#endif
2309
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002310private:
2311 const SkPoint* fPts;
2312 SkPath::Verb fVerb;
2313 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002314 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002315 int fDoneSpans; // used for quick check that segment is finished
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002316#if DEBUG_DUMP
2317 int fID;
2318#endif
2319};
2320
caryclark@google.comb9738012012-07-03 19:53:30 +00002321class Contour;
2322
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002323struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00002324 Contour* fContours[2];
2325 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002326 double fTs[2][2];
2327};
2328
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002329class Contour {
2330public:
2331 Contour() {
2332 reset();
2333#if DEBUG_DUMP
2334 fID = ++gContourID;
2335#endif
2336 }
2337
2338 bool operator<(const Contour& rh) const {
2339 return fBounds.fTop == rh.fBounds.fTop
2340 ? fBounds.fLeft < rh.fBounds.fLeft
2341 : fBounds.fTop < rh.fBounds.fTop;
2342 }
2343
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002344 void addCoincident(int index, Contour* other, int otherIndex,
2345 const Intersections& ts, bool swap) {
2346 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00002347 coincidence.fContours[0] = this;
2348 coincidence.fContours[1] = other;
2349 coincidence.fSegments[0] = index;
2350 coincidence.fSegments[1] = otherIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002351 coincidence.fTs[swap][0] = ts.fT[0][0];
2352 coincidence.fTs[swap][1] = ts.fT[0][1];
2353 coincidence.fTs[!swap][0] = ts.fT[1][0];
2354 coincidence.fTs[!swap][1] = ts.fT[1][1];
2355 }
2356
2357 void addCross(const Contour* crosser) {
2358#ifdef DEBUG_CROSS
2359 for (int index = 0; index < fCrosses.count(); ++index) {
2360 SkASSERT(fCrosses[index] != crosser);
2361 }
2362#endif
2363 *fCrosses.append() = crosser;
2364 }
2365
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002366 void addCubic(const SkPoint pts[4]) {
2367 fSegments.push_back().addCubic(pts);
2368 fContainsCurves = true;
2369 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002370
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002371 int addLine(const SkPoint pts[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002372 fSegments.push_back().addLine(pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002373 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002374 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002375
2376 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
2377 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
2378 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002379
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002380 int addQuad(const SkPoint pts[3]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002381 fSegments.push_back().addQuad(pts);
2382 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002383 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002384 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002385
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002386 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
2387 containsIntercepts();
2388 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
2389 }
2390
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002391 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002392 return fBounds;
2393 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002394
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002395 void complete() {
2396 setBounds();
2397 fContainsIntercepts = false;
2398 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002399
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002400 void containsIntercepts() {
2401 fContainsIntercepts = true;
2402 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002403
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002404 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
2405 int &tIndex, double& hitT) {
2406 int segmentCount = fSegments.count();
2407 const Segment* bestSegment = NULL;
2408 for (int test = 0; test < segmentCount; ++test) {
2409 Segment* testSegment = &fSegments[test];
2410 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002411 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002412 continue;
2413 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002414 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002415 continue;
2416 }
2417 if (bounds.fLeft > basePt.fX) {
2418 continue;
2419 }
2420 if (bounds.fRight < basePt.fX) {
2421 continue;
2422 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002423 if (bounds.fLeft == bounds.fRight) {
2424 continue;
2425 }
2426 #if 0
2427 bool leftHalf = bounds.fLeft == basePt.fX;
2428 bool rightHalf = bounds.fRight == basePt.fX;
2429 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
2430 basePt, leftHalf, rightHalf)) {
2431 continue;
2432 }
2433 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002434 double testHitT;
2435 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
2436 if (testT >= 0) {
2437 bestSegment = testSegment;
2438 tIndex = testT;
2439 hitT = testHitT;
2440 }
2441 }
2442 return bestSegment;
2443 }
2444
2445 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002446 for (int index = 0; index < fCrosses.count(); ++index) {
2447 if (fCrosses[index] == crosser) {
2448 return true;
2449 }
2450 }
2451 return false;
2452 }
2453
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002454 void findTooCloseToCall(int winding) {
2455 int segmentCount = fSegments.count();
2456 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
2457 fSegments[sIndex].findTooCloseToCall(winding);
2458 }
2459 }
2460
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002461 void fixOtherTIndex() {
2462 int segmentCount = fSegments.count();
2463 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
2464 fSegments[sIndex].fixOtherTIndex();
2465 }
2466 }
2467
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002468 void reset() {
2469 fSegments.reset();
2470 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002471 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002472 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002473
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002474 void resolveCoincidence(int winding) {
2475 int count = fCoincidences.count();
2476 for (int index = 0; index < count; ++index) {
2477 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00002478 Contour* thisContour = coincidence.fContours[0];
2479 Contour* otherContour = coincidence.fContours[1];
2480 int thisIndex = coincidence.fSegments[0];
2481 int otherIndex = coincidence.fSegments[1];
2482 Segment& thisOne = thisContour->fSegments[thisIndex];
2483 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00002484 #if DEBUG_CONCIDENT
2485 thisOne.debugShowTs();
2486 other.debugShowTs();
2487 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002488 double startT = coincidence.fTs[0][0];
2489 double endT = coincidence.fTs[0][1];
2490 if (startT > endT) {
2491 SkTSwap<double>(startT, endT);
2492 }
2493 SkASSERT(endT - startT >= FLT_EPSILON);
2494 double oStartT = coincidence.fTs[1][0];
2495 double oEndT = coincidence.fTs[1][1];
2496 if (oStartT > oEndT) {
2497 SkTSwap<double>(oStartT, oEndT);
2498 }
2499 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
caryclark@google.comb9738012012-07-03 19:53:30 +00002500 if (winding > 0 || thisOne.cancels(other)) {
2501 // make sure startT and endT have t entries
2502 if (thisOne.isMissing(startT) || other.isMissing(oEndT)) {
2503 thisOne.addTPair(startT, other, oEndT);
2504 }
2505 if (thisOne.isMissing(endT) || other.isMissing(oStartT)) {
2506 other.addTPair(oStartT, thisOne, endT);
2507 }
2508 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002509 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00002510 if (startT > 0 || oStartT > 0
2511 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002512 thisOne.addTPair(startT, other, oStartT);
2513 }
caryclark@google.com200c2112012-08-03 15:05:04 +00002514 if (endT < 1 || oEndT < 1
2515 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002516 other.addTPair(oEndT, thisOne, endT);
2517 }
2518 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002519 }
caryclark@google.com47580692012-07-23 12:14:49 +00002520 #if DEBUG_CONCIDENT
2521 thisOne.debugShowTs();
2522 other.debugShowTs();
2523 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002524 }
2525 }
2526
2527 const SkTArray<Segment>& segments() {
2528 return fSegments;
2529 }
2530
caryclark@google.com15fa1382012-05-07 20:49:36 +00002531 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
2532 // we need to sort and walk edges in y, but that on the surface opens the
2533 // same can of worms as before. But then, this is a rough sort based on
2534 // segments' top, and not a true sort, so it could be ameniable to regular
2535 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002536 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002537 int segmentCount = fSegments.count();
2538 SkASSERT(segmentCount > 0);
2539 int best = -1;
2540 Segment* bestSegment = NULL;
2541 while (++best < segmentCount) {
2542 Segment* testSegment = &fSegments[best];
2543 if (testSegment->done()) {
2544 continue;
2545 }
2546 bestSegment = testSegment;
2547 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002548 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002549 if (!bestSegment) {
2550 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002551 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002552 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002553 for (int test = best + 1; test < segmentCount; ++test) {
2554 Segment* testSegment = &fSegments[test];
2555 if (testSegment->done()) {
2556 continue;
2557 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002558 if (testSegment->bounds().fTop > bestTop) {
2559 continue;
2560 }
2561 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002562 if (bestTop > testTop) {
2563 bestTop = testTop;
2564 bestSegment = testSegment;
2565 }
2566 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002567 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002568 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002569 }
2570
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002571 int updateSegment(int index, const SkPoint* pts) {
2572 Segment& segment = fSegments[index];
2573 segment.updatePts(pts);
2574 return segment.verb() + 1;
2575 }
2576
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002577#if DEBUG_TEST
2578 SkTArray<Segment>& debugSegments() {
2579 return fSegments;
2580 }
2581#endif
2582
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002583#if DEBUG_DUMP
2584 void dump() {
2585 int i;
2586 const char className[] = "Contour";
2587 const int tab = 4;
2588 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
2589 for (i = 0; i < fSegments.count(); ++i) {
2590 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
2591 className, i);
2592 fSegments[i].dump();
2593 }
2594 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
2595 tab + sizeof(className), className,
2596 fBounds.fLeft, fBounds.fTop,
2597 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002598 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
2599 className, fContainsIntercepts);
2600 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
2601 className, fContainsCurves);
2602 }
2603#endif
2604
caryclark@google.com027de222012-07-12 12:52:50 +00002605#if DEBUG_ACTIVE_SPANS
2606 void debugShowActiveSpans() {
2607 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002608 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00002609 }
2610 }
2611#endif
2612
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002613protected:
2614 void setBounds() {
2615 int count = fSegments.count();
2616 if (count == 0) {
2617 SkDebugf("%s empty contour\n", __FUNCTION__);
2618 SkASSERT(0);
2619 // FIXME: delete empty contour?
2620 return;
2621 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002622 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002623 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002624 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002625 }
2626 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002627
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002628private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002629 SkTArray<Segment> fSegments;
2630 SkTDArray<Coincidence> fCoincidences;
2631 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002632 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002633 bool fContainsIntercepts;
2634 bool fContainsCurves;
2635#if DEBUG_DUMP
2636 int fID;
2637#endif
2638};
2639
2640class EdgeBuilder {
2641public:
2642
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002643EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002644 : fPath(path)
2645 , fCurrentContour(NULL)
2646 , fContours(contours)
2647{
2648#if DEBUG_DUMP
2649 gContourID = 0;
2650 gSegmentID = 0;
2651#endif
2652 walk();
2653}
2654
2655protected:
2656
2657void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002658 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002659 fCurrentContour->complete();
2660 fCurrentContour = NULL;
2661 }
2662}
2663
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002664void walk() {
2665 // FIXME:remove once we can access path pts directly
2666 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
2667 SkPoint pts[4];
2668 SkPath::Verb verb;
2669 do {
2670 verb = iter.next(pts);
2671 *fPathVerbs.append() = verb;
2672 if (verb == SkPath::kMove_Verb) {
2673 *fPathPts.append() = pts[0];
2674 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
2675 fPathPts.append(verb, &pts[1]);
2676 }
2677 } while (verb != SkPath::kDone_Verb);
2678 // FIXME: end of section to remove once path pts are accessed directly
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002679
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002680 SkPath::Verb reducedVerb;
2681 uint8_t* verbPtr = fPathVerbs.begin();
2682 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002683 const SkPoint* finalCurveStart = NULL;
2684 const SkPoint* finalCurveEnd = NULL;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002685 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
2686 switch (verb) {
2687 case SkPath::kMove_Verb:
2688 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002689 if (!fCurrentContour) {
2690 fCurrentContour = fContours.push_back_n(1);
2691 finalCurveEnd = pointsPtr++;
2692 *fExtra.append() = -1; // start new contour
2693 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002694 continue;
2695 case SkPath::kLine_Verb:
2696 // skip degenerate points
2697 if (pointsPtr[-1].fX != pointsPtr[0].fX
2698 || pointsPtr[-1].fY != pointsPtr[0].fY) {
2699 fCurrentContour->addLine(&pointsPtr[-1]);
2700 }
2701 break;
2702 case SkPath::kQuad_Verb:
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002703
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002704 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
2705 if (reducedVerb == 0) {
2706 break; // skip degenerate points
2707 }
2708 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002709 *fExtra.append() =
2710 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002711 break;
2712 }
2713 fCurrentContour->addQuad(&pointsPtr[-1]);
2714 break;
2715 case SkPath::kCubic_Verb:
2716 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
2717 if (reducedVerb == 0) {
2718 break; // skip degenerate points
2719 }
2720 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002721 *fExtra.append() =
2722 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002723 break;
2724 }
2725 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002726 *fExtra.append() =
2727 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002728 break;
2729 }
2730 fCurrentContour->addCubic(&pointsPtr[-1]);
2731 break;
2732 case SkPath::kClose_Verb:
2733 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002734 if (finalCurveStart && finalCurveEnd
2735 && *finalCurveStart != *finalCurveEnd) {
2736 *fReducePts.append() = *finalCurveStart;
2737 *fReducePts.append() = *finalCurveEnd;
2738 *fExtra.append() =
2739 fCurrentContour->addLine(fReducePts.end() - 2);
2740 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002741 complete();
2742 continue;
2743 default:
2744 SkDEBUGFAIL("bad verb");
2745 return;
2746 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002747 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002748 pointsPtr += verb;
2749 SkASSERT(fCurrentContour);
2750 }
2751 complete();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002752 if (fCurrentContour && !fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002753 fContours.pop_back();
2754 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002755 // correct pointers in contours since fReducePts may have moved as it grew
2756 int cIndex = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002757 int extraCount = fExtra.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002758 SkASSERT(extraCount == 0 || fExtra[0] == -1);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002759 int eIndex = 0;
2760 int rIndex = 0;
2761 while (++eIndex < extraCount) {
2762 int offset = fExtra[eIndex];
2763 if (offset < 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002764 ++cIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002765 continue;
2766 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002767 fCurrentContour = &fContours[cIndex];
2768 rIndex += fCurrentContour->updateSegment(offset - 1,
2769 &fReducePts[rIndex]);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002770 }
2771 fExtra.reset(); // we're done with this
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002772}
2773
2774private:
2775 const SkPath& fPath;
2776 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002777 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002778 Contour* fCurrentContour;
2779 SkTArray<Contour>& fContours;
2780 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002781 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002782};
2783
2784class Work {
2785public:
2786 enum SegmentType {
2787 kHorizontalLine_Segment = -1,
2788 kVerticalLine_Segment = 0,
2789 kLine_Segment = SkPath::kLine_Verb,
2790 kQuad_Segment = SkPath::kQuad_Verb,
2791 kCubic_Segment = SkPath::kCubic_Verb,
2792 };
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002793
2794 void addCoincident(Work& other, const Intersections& ts, bool swap) {
2795 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
2796 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002797
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002798 // FIXME: does it make sense to write otherIndex now if we're going to
2799 // fix it up later?
2800 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002801 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002802 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002803
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002804 // Avoid collapsing t values that are close to the same since
2805 // we walk ts to describe consecutive intersections. Since a pair of ts can
2806 // be nearly equal, any problems caused by this should be taken care
2807 // of later.
2808 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002809 int addT(double newT, const Work& other) {
2810 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002811 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002812
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002813 bool advance() {
2814 return ++fIndex < fLast;
2815 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002816
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002817 SkScalar bottom() const {
2818 return bounds().fBottom;
2819 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002820
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002821 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002822 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002823 }
2824
2825 const SkPoint* cubic() const {
2826 return fCubic;
2827 }
2828
2829 void init(Contour* contour) {
2830 fContour = contour;
2831 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002832 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002833 }
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002834
2835 bool isAdjacent(const Work& next) {
2836 return fContour == next.fContour && fIndex + 1 == next.fIndex;
2837 }
2838
2839 bool isFirstLast(const Work& next) {
2840 return fContour == next.fContour && fIndex == 0
2841 && next.fIndex == fLast - 1;
2842 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002843
2844 SkScalar left() const {
2845 return bounds().fLeft;
2846 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002847
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002848 void promoteToCubic() {
2849 fCubic[0] = pts()[0];
2850 fCubic[2] = pts()[1];
2851 fCubic[3] = pts()[2];
2852 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
2853 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
2854 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
2855 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
2856 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002857
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002858 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002859 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002860 }
2861
2862 SkScalar right() const {
2863 return bounds().fRight;
2864 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002865
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002866 ptrdiff_t segmentIndex() const {
2867 return fIndex;
2868 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002869
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002870 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002871 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002872 SegmentType type = (SegmentType) segment.verb();
2873 if (type != kLine_Segment) {
2874 return type;
2875 }
2876 if (segment.isHorizontal()) {
2877 return kHorizontalLine_Segment;
2878 }
2879 if (segment.isVertical()) {
2880 return kVerticalLine_Segment;
2881 }
2882 return kLine_Segment;
2883 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002884
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002885 bool startAfter(const Work& after) {
2886 fIndex = after.fIndex;
2887 return advance();
2888 }
2889
2890 SkScalar top() const {
2891 return bounds().fTop;
2892 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002893
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002894 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002895 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002896 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002897
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002898 SkScalar x() const {
2899 return bounds().fLeft;
2900 }
2901
2902 bool xFlipped() const {
2903 return x() != pts()[0].fX;
2904 }
2905
2906 SkScalar y() const {
2907 return bounds().fTop;
2908 }
2909
2910 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002911 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002912 }
2913
2914protected:
2915 Contour* fContour;
2916 SkPoint fCubic[4];
2917 int fIndex;
2918 int fLast;
2919};
2920
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002921#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002922static void debugShowLineIntersection(int pts, const Work& wt,
2923 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002924 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002925 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
2926 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
2927 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
2928 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002929 return;
2930 }
2931 SkPoint wtOutPt, wnOutPt;
2932 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
2933 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002934 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002935 __FUNCTION__,
2936 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
2937 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
2938 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002939 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002940 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002941 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002942 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
2943 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
2944 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002945 SkDebugf(" wnTs[1]=%g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002946 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002947 SkDebugf("\n");
2948}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002949#else
2950static void debugShowLineIntersection(int , const Work& ,
2951 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002952}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002953#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002954
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002955static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002956
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002957 if (test != next) {
2958 if (test->bounds().fBottom < next->bounds().fTop) {
2959 return false;
2960 }
2961 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
2962 return true;
2963 }
2964 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002965 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002966 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002967 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002968 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002969 Work wn;
2970 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002971 if (test == next && !wn.startAfter(wt)) {
2972 continue;
2973 }
2974 do {
2975 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
2976 continue;
2977 }
2978 int pts;
2979 Intersections ts;
2980 bool swap = false;
2981 switch (wt.segmentType()) {
2982 case Work::kHorizontalLine_Segment:
2983 swap = true;
2984 switch (wn.segmentType()) {
2985 case Work::kHorizontalLine_Segment:
2986 case Work::kVerticalLine_Segment:
2987 case Work::kLine_Segment: {
2988 pts = HLineIntersect(wn.pts(), wt.left(),
2989 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002990 debugShowLineIntersection(pts, wt, wn,
2991 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002992 break;
2993 }
2994 case Work::kQuad_Segment: {
2995 pts = HQuadIntersect(wn.pts(), wt.left(),
2996 wt.right(), wt.y(), wt.xFlipped(), ts);
2997 break;
2998 }
2999 case Work::kCubic_Segment: {
3000 pts = HCubicIntersect(wn.pts(), wt.left(),
3001 wt.right(), wt.y(), wt.xFlipped(), ts);
3002 break;
3003 }
3004 default:
3005 SkASSERT(0);
3006 }
3007 break;
3008 case Work::kVerticalLine_Segment:
3009 swap = true;
3010 switch (wn.segmentType()) {
3011 case Work::kHorizontalLine_Segment:
3012 case Work::kVerticalLine_Segment:
3013 case Work::kLine_Segment: {
3014 pts = VLineIntersect(wn.pts(), wt.top(),
3015 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003016 debugShowLineIntersection(pts, wt, wn,
3017 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003018 break;
3019 }
3020 case Work::kQuad_Segment: {
3021 pts = VQuadIntersect(wn.pts(), wt.top(),
3022 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3023 break;
3024 }
3025 case Work::kCubic_Segment: {
3026 pts = VCubicIntersect(wn.pts(), wt.top(),
3027 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3028 break;
3029 }
3030 default:
3031 SkASSERT(0);
3032 }
3033 break;
3034 case Work::kLine_Segment:
3035 switch (wn.segmentType()) {
3036 case Work::kHorizontalLine_Segment:
3037 pts = HLineIntersect(wt.pts(), wn.left(),
3038 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003039 debugShowLineIntersection(pts, wt, wn,
3040 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003041 break;
3042 case Work::kVerticalLine_Segment:
3043 pts = VLineIntersect(wt.pts(), wn.top(),
3044 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003045 debugShowLineIntersection(pts, wt, wn,
3046 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003047 break;
3048 case Work::kLine_Segment: {
3049 pts = LineIntersect(wt.pts(), wn.pts(), ts);
3050 debugShowLineIntersection(pts, wt, wn,
3051 ts.fT[1], ts.fT[0]);
3052 break;
3053 }
3054 case Work::kQuad_Segment: {
3055 swap = true;
3056 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
3057 break;
3058 }
3059 case Work::kCubic_Segment: {
3060 swap = true;
3061 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
3062 break;
3063 }
3064 default:
3065 SkASSERT(0);
3066 }
3067 break;
3068 case Work::kQuad_Segment:
3069 switch (wn.segmentType()) {
3070 case Work::kHorizontalLine_Segment:
3071 pts = HQuadIntersect(wt.pts(), wn.left(),
3072 wn.right(), wn.y(), wn.xFlipped(), ts);
3073 break;
3074 case Work::kVerticalLine_Segment:
3075 pts = VQuadIntersect(wt.pts(), wn.top(),
3076 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3077 break;
3078 case Work::kLine_Segment: {
3079 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
3080 break;
3081 }
3082 case Work::kQuad_Segment: {
3083 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
3084 break;
3085 }
3086 case Work::kCubic_Segment: {
3087 wt.promoteToCubic();
3088 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
3089 break;
3090 }
3091 default:
3092 SkASSERT(0);
3093 }
3094 break;
3095 case Work::kCubic_Segment:
3096 switch (wn.segmentType()) {
3097 case Work::kHorizontalLine_Segment:
3098 pts = HCubicIntersect(wt.pts(), wn.left(),
3099 wn.right(), wn.y(), wn.xFlipped(), ts);
3100 break;
3101 case Work::kVerticalLine_Segment:
3102 pts = VCubicIntersect(wt.pts(), wn.top(),
3103 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3104 break;
3105 case Work::kLine_Segment: {
3106 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
3107 break;
3108 }
3109 case Work::kQuad_Segment: {
3110 wn.promoteToCubic();
3111 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
3112 break;
3113 }
3114 case Work::kCubic_Segment: {
3115 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
3116 break;
3117 }
3118 default:
3119 SkASSERT(0);
3120 }
3121 break;
3122 default:
3123 SkASSERT(0);
3124 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003125 if (!foundCommonContour && pts > 0) {
3126 test->addCross(next);
3127 next->addCross(test);
3128 foundCommonContour = true;
3129 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003130 // in addition to recording T values, record matching segment
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003131 if (pts == 2 && wn.segmentType() <= Work::kLine_Segment
3132 && wt.segmentType() <= Work::kLine_Segment) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003133 wt.addCoincident(wn, ts, swap);
3134 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003135 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003136 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003137 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
3138 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003139 int testTAt = wt.addT(ts.fT[swap][pt], wn);
3140 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003141 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
3142 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003143 }
3144 } while (wn.advance());
3145 } while (wt.advance());
3146 return true;
3147}
3148
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003149// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003150// see if coincidence is formed by clipping non-concident segments
3151static void coincidenceCheck(SkTDArray<Contour*>& contourList, int winding) {
3152 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00003153 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003154 Contour* contour = contourList[cIndex];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003155 contour->findTooCloseToCall(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003156 }
3157 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
3158 Contour* contour = contourList[cIndex];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003159 contour->resolveCoincidence(winding);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003160 }
3161}
3162
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003163// project a ray from the top of the contour up and see if it hits anything
3164// note: when we compute line intersections, we keep track of whether
3165// two contours touch, so we need only look at contours not touching this one.
3166// OPTIMIZATION: sort contourList vertically to avoid linear walk
3167static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003168 const Segment* current, int index, int endIndex) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003169 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003170 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003171 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00003172 const Segment* test = NULL;
3173 int tIndex;
3174 double tHit;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003175 // bool checkCrosses = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003176 for (int cTest = 0; cTest < contourCount; ++cTest) {
3177 Contour* contour = contourList[cTest];
3178 if (basePt.fY < contour->bounds().fTop) {
3179 continue;
3180 }
3181 if (bestY > contour->bounds().fBottom) {
3182 continue;
3183 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003184#if 0
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003185 // even though the contours crossed, if spans cancel through concidence,
3186 // the contours may be not have any span links to chase, and the current
3187 // segment may be isolated. Detect this by seeing if current has
3188 // uninitialized wind sums. If so, project a ray instead of relying on
3189 // previously found intersections.
3190 if (baseContour == contour) {
3191 continue;
3192 }
3193 if (checkCrosses && baseContour->crosses(contour)) {
3194 if (current->isConnected(index, endIndex)) {
3195 continue;
3196 }
3197 checkCrosses = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003198 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003199#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003200 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
3201 if (next) {
3202 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003203 }
caryclark@google.com47580692012-07-23 12:14:49 +00003204 }
3205 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00003206 return 0;
3207 }
3208 int winding, windValue;
3209 // If the ray hit the end of a span, we need to construct the wheel of
3210 // angles to find the span closest to the ray -- even if there are just
3211 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003212 const Angle* angle = NULL;
caryclark@google.come21cb182012-07-23 21:26:31 +00003213 if (fabs(tHit - test->t(tIndex)) < FLT_EPSILON) {
caryclark@google.com47580692012-07-23 12:14:49 +00003214 SkTDArray<Angle> angles;
3215 int end = test->nextSpan(tIndex, 1);
3216 if (end < 0) {
3217 end = test->nextSpan(tIndex, -1);
3218 }
3219 test->addTwoAngles(end, tIndex, angles);
3220 test->buildAngles(tIndex, angles);
3221 SkTDArray<Angle*> sorted;
3222 // OPTIMIZATION: call a sort that, if base point is the leftmost,
3223 // returns the first counterclockwise hour before 6 o'clock,
3224 // or if the base point is rightmost, returns the first clockwise
3225 // hour after 6 o'clock
3226 sortAngles(angles, sorted);
3227#if DEBUG_SORT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003228 sorted[0]->segment()->debugShowSort(sorted, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00003229#endif
3230 // walk the sorted angle fan to find the lowest angle
3231 // above the base point. Currently, the first angle in the sorted array
3232 // is 12 noon or an earlier hour (the next counterclockwise)
3233 int count = sorted.count();
3234 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003235 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00003236 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003237 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00003238 for (int index = 0; index < count; ++index) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003239 const Angle* angle = sorted[index];
3240 if (baseMatches && angle->isHorizontal()) {
3241 continue;
3242 }
3243 double indexDx = angle->dx();
caryclark@google.com47580692012-07-23 12:14:49 +00003244 if (indexDx < 0) {
3245 left = index;
3246 } else if (indexDx > 0) {
3247 right = index;
3248 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003249 } else {
3250 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003251 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003252 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003253 if (left < 0 && right < 0) {
3254 left = mid;
3255 }
caryclark@google.com47580692012-07-23 12:14:49 +00003256 SkASSERT(left >= 0 || right >= 0);
3257 if (left < 0) {
3258 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003259 } else if (left >= 0 && mid >= 0 && right >= 0
3260 && sorted[mid]->sign() == sorted[right]->sign()) {
3261 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003262 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003263 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00003264 test = angle->segment();
3265 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00003266 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003267 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00003268#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003269 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
3270 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00003271#endif
3272 } else {
3273 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00003274 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003275 windValue = test->windValue(tIndex);
3276#if DEBUG_WINDING
3277 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
3278 windValue);
3279#endif
3280 }
3281 // see if a + change in T results in a +/- change in X (compute x'(T))
3282 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
3283#if DEBUG_WINDING
3284 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
3285#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003286 if (dx == 0) {
3287 SkASSERT(angle);
3288 if (test->firstBump(angle, winding)) {
3289 winding -= test->windBump(angle);
3290 }
3291 } else if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00003292 winding += dx > 0 ? -windValue : windValue;
3293#if DEBUG_WINDING
3294 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
3295#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003296 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003297 // start here;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003298 // we're broken because we find a vertical span
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003299 return winding;
3300}
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003301
3302// OPTIMIZATION: not crazy about linear search here to find top active y.
3303// seems like we should break down and do the sort, or maybe sort each
3304// contours' segments?
3305// Once the segment array is built, there's no reason I can think of not to
3306// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003307// FIXME: return the contour found to pass to inner contour check
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003308static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003309 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003310 int cIndex = 0;
3311 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003312 SkScalar bestY = SK_ScalarMax;
3313 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003314 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003315 contour = contourList[cIndex];
3316 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003317 } while (!topStart && ++cIndex < contourCount);
3318 if (!topStart) {
3319 return NULL;
3320 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003321 while (++cIndex < contourCount) {
3322 contour = contourList[cIndex];
3323 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003324 continue;
3325 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003326 SkScalar testY = SK_ScalarMax;
3327 Segment* test = contour->topSegment(testY);
3328 if (!test || bestY <= testY) {
3329 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003330 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003331 topStart = test;
3332 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003333 }
3334 return topStart;
3335}
3336
caryclark@google.come21cb182012-07-23 21:26:31 +00003337static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
3338 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003339 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00003340 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003341 const Span& backPtr = span->fOther->span(span->fOtherIndex);
3342 Segment* segment = backPtr.fOther;
3343 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003344 SkTDArray<Angle> angles;
3345 int done = 0;
3346 if (segment->activeAngle(tIndex, done, angles)) {
3347 Angle* last = angles.end() - 1;
3348 tIndex = last->start();
3349 endIndex = last->end();
3350 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003351 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00003352 if (done == angles.count()) {
3353 chase.pop(&span);
3354 continue;
3355 }
3356 SkTDArray<Angle*> sorted;
3357 sortAngles(angles, sorted);
3358 // find first angle, initialize winding to computed fWindSum
3359 int firstIndex = -1;
3360 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003361 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003362 do {
3363 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003364 segment = angle->segment();
3365 winding = segment->windSum(angle);
3366 } while (winding == SK_MinS32);
3367 int spanWinding = segment->spanSign(angle->start(), angle->end());
3368 #if DEBUG_WINDING
3369 SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
3370 __FUNCTION__, winding, spanWinding, contourWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00003371 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003372 // turn swinding into contourWinding
3373 if (spanWinding * winding < 0) {
3374 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003375 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003376 #if DEBUG_SORT
3377 segment->debugShowSort(sorted, firstIndex, winding);
3378 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003379 // we care about first sign and whether wind sum indicates this
3380 // edge is inside or outside. Maybe need to pass span winding
3381 // or first winding or something into this function?
3382 // advance to first undone angle, then return it and winding
3383 // (to set whether edges are active or not)
3384 int nextIndex = firstIndex + 1;
3385 int angleCount = sorted.count();
3386 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003387 angle = sorted[firstIndex];
3388 winding -= angle->segment()->windBump(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00003389 do {
3390 SkASSERT(nextIndex != firstIndex);
3391 if (nextIndex == angleCount) {
3392 nextIndex = 0;
3393 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003394 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00003395 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003396 int maxWinding = winding;
3397 winding -= segment->windBump(angle);
3398 #if DEBUG_SORT
3399 SkDebugf("%s id=%d maxWinding=%d winding=%d\n", __FUNCTION__,
3400 segment->debugID(), maxWinding, winding);
3401 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003402 tIndex = angle->start();
3403 endIndex = angle->end();
3404 int lesser = SkMin32(tIndex, endIndex);
3405 const Span& nextSpan = segment->span(lesser);
3406 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003407#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00003408 // FIXME: this be wrong. assign startWinding if edge is in
3409 // same direction. If the direction is opposite, winding to
3410 // assign is flipped sign or +/- 1?
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003411 if (abs(maxWinding) < abs(winding)) {
3412 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003413 }
3414 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003415#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003416 break;
3417 }
3418 } while (++nextIndex != lastIndex);
3419 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003420 }
3421 return NULL;
3422}
3423
caryclark@google.com027de222012-07-12 12:52:50 +00003424#if DEBUG_ACTIVE_SPANS
3425static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
3426 for (int index = 0; index < contourList.count(); ++ index) {
3427 contourList[index]->debugShowActiveSpans();
3428 }
3429}
3430#endif
3431
caryclark@google.com27c449a2012-07-27 18:26:38 +00003432static bool windingIsActive(int winding, int spanWinding) {
3433 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
3434 && (!winding || !spanWinding || winding == -spanWinding);
3435}
3436
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003437// Each segment may have an inside or an outside. Segments contained within
3438// winding may have insides on either side, and form a contour that should be
3439// ignored. Segments that are coincident with opposing direction segments may
3440// have outsides on either side, and should also disappear.
3441// 'Normal' segments will have one inside and one outside. Subsequent connections
3442// when winding should follow the intersection direction. If more than one edge
3443// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003444 // since we start with leftmost top edge, we'll traverse through a
3445 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003446static void bridge(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003447 bool firstContour = true;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003448 do {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003449 Segment* topStart = findTopContour(contourList);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003450 if (!topStart) {
3451 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00003452 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003453 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00003454 // follow edges to intersection by changing the index by direction.
3455 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003456 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003457 int contourWinding;
3458 if (firstContour) {
3459 contourWinding = 0;
3460 firstContour = false;
3461 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003462 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003463 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00003464 if (sumWinding == SK_MinS32) {
3465 sumWinding = current->computeSum(index, endIndex);
3466 }
3467 if (sumWinding == SK_MinS32) {
3468 contourWinding = innerContourCheck(contourList, current,
3469 index, endIndex);
3470 } else {
3471 contourWinding = sumWinding;
3472 int spanWinding = current->spanSign(index, endIndex);
3473 if (spanWinding * sumWinding > 0) {
3474 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003475 }
3476 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003477#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003478 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003479 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
3480#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003481 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003482 SkPoint lastPt;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003483 bool firstTime = true;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003484 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003485 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003486 // FIXME: needs work. While it works in limited situations, it does
3487 // not always compute winding correctly. Active should be removed and instead
3488 // the initial winding should be correctly passed in so that if the
3489 // inner contour is wound the same way, it never finds an accumulated
3490 // winding of zero. Inside 'find next', we need to look for transitions
3491 // other than zero when resolving sorted angles.
caryclark@google.com27c449a2012-07-27 18:26:38 +00003492 bool active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003493 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003494 do {
caryclark@google.com0e08a192012-07-13 21:07:52 +00003495 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00003496 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
3497 __FUNCTION__, active ? "true" : "false",
3498 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003499 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003500 const SkPoint* firstPt = NULL;
3501 do {
3502 SkASSERT(!current->done());
caryclark@google.comafe56de2012-07-24 18:11:03 +00003503 int nextStart, nextEnd;
caryclark@google.com27c449a2012-07-27 18:26:38 +00003504 Segment* next = current->findNext(chaseArray,
3505 firstTime, active, index, endIndex,
3506 nextStart, nextEnd, winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003507 if (!next) {
3508 break;
3509 }
3510 if (!firstPt) {
3511 firstPt = &current->addMoveTo(index, simple, active);
3512 }
3513 lastPt = current->addCurveTo(index, endIndex, simple, active);
3514 current = next;
3515 index = nextStart;
3516 endIndex = nextEnd;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003517 firstTime = false;
3518 } while (*firstPt != lastPt && (active || !current->done()));
3519 if (firstPt && active) {
3520 #if DEBUG_PATH_CONSTRUCTION
3521 SkDebugf("%s close\n", __FUNCTION__);
3522 #endif
3523 simple.close();
3524 }
caryclark@google.come21cb182012-07-23 21:26:31 +00003525 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003526 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00003527 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003528 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003529 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003530 break;
3531 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003532 int lesser = SkMin32(index, endIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003533 spanWinding = current->spanSign(index, endIndex);
3534 winding = current->windSum(lesser);
3535 if (spanWinding * winding > 0) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003536 winding -= spanWinding;
3537 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003538 active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003539 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003540 } while (true);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003541}
3542
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003543static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
3544 int contourCount = contourList.count();
3545 for (int cTest = 0; cTest < contourCount; ++cTest) {
3546 Contour* contour = contourList[cTest];
3547 contour->fixOtherTIndex();
3548 }
3549}
3550
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003551static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003552 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003553 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003554 if (count == 0) {
3555 return;
3556 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003557 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003558 *list.append() = &contours[index];
3559 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003560 QSort<Contour>(list.begin(), list.end() - 1);
3561}
3562
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003563void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003564 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003565 int winding = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003566 simple.reset();
3567 simple.setFillType(SkPath::kEvenOdd_FillType);
3568
3569 // turn path into list of segments
3570 SkTArray<Contour> contours;
3571 // FIXME: add self-intersecting cubics' T values to segment
3572 EdgeBuilder builder(path, contours);
3573 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003574 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003575 Contour** currentPtr = contourList.begin();
3576 if (!currentPtr) {
3577 return;
3578 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003579 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003580 // find all intersections between segments
3581 do {
3582 Contour** nextPtr = currentPtr;
3583 Contour* current = *currentPtr++;
3584 Contour* next;
3585 do {
3586 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003587 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003588 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003589 // eat through coincident edges
3590 coincidenceCheck(contourList, winding);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003591 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003592 // construct closed contours
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003593 bridge(contourList, simple);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003594}
3595