blob: 45f969677040b02c3f85abb4546196ffe0482d52 [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.com03f97062012-08-21 13:13:52 +000030#if 1 // 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.com24bec792012-08-20 12:43:57 +000051#define DEBUG_ADD_INTERSECTING_TS 1
52#define DEBUG_ADD_T_PAIR 0
caryclark@google.com200c2112012-08-03 15:05:04 +000053#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.com03f97062012-08-21 13:13:52 +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.com24bec792012-08-20 12:43:57 +0000362 if (order == 2) { // quad became line
363 for (int index = 0; index < order; ++index) {
364 SkPoint* pt = reducePts.append();
365 pt->fX = SkDoubleToScalar(dst[index].x);
366 pt->fY = SkDoubleToScalar(dst[index].y);
367 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000368 }
369 return (SkPath::Verb) (order - 1);
370}
371
372static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
373 SkTDArray<SkPoint>& reducePts) {
374 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
375 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
376 Cubic dst;
377 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000378 if (order == 2 || order == 3) { // cubic became line or quad
379 for (int index = 0; index < order; ++index) {
380 SkPoint* pt = reducePts.append();
381 pt->fX = SkDoubleToScalar(dst[index].x);
382 pt->fY = SkDoubleToScalar(dst[index].y);
383 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000384 }
385 return (SkPath::Verb) (order - 1);
386}
387
caryclark@google.com15fa1382012-05-07 20:49:36 +0000388static bool QuadIsLinear(const SkPoint a[3]) {
389 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
390 {a[2].fX, a[2].fY}};
391 return isLinear(aQuad, 0, 2);
392}
393
394static bool CubicIsLinear(const SkPoint a[4]) {
395 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
396 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
397 return isLinear(aCubic, 0, 3);
398}
399
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000400static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
401 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
402 double x[2];
403 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000404 xy_at_t(aLine, endT, x[1], *(double*) 0);
405 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000406}
407
408static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
409 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
410 {a[2].fX, a[2].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000411 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000412}
413
414static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
415 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
416 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000417 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000418}
419
420static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
421 NULL,
422 LineLeftMost,
423 QuadLeftMost,
424 CubicLeftMost
425};
426
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000427#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000428static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
429 const SkPoint& below) {
430 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
431 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
432 return implicit_matches_ulps(aLine, bLine, 32);
433}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000434#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000435
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000436class Segment;
437
caryclark@google.com15fa1382012-05-07 20:49:36 +0000438// sorting angles
439// given angles of {dx dy ddx ddy dddx dddy} sort them
440class Angle {
441public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000442 // FIXME: this is bogus for quads and cubics
443 // if the quads and cubics' line from end pt to ctrl pt are coincident,
444 // there's no obvious way to determine the curve ordering from the
445 // derivatives alone. In particular, if one quadratic's coincident tangent
446 // is longer than the other curve, the final control point can place the
447 // longer curve on either side of the shorter one.
448 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
449 // may provide some help, but nothing has been figured out yet.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000450 bool operator<(const Angle& rh) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000451 if ((fDy < 0) ^ (rh.fDy < 0)) {
452 return fDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000453 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000454 if (fDy == 0 && rh.fDy == 0 && fDx != rh.fDx) {
455 return fDx < rh.fDx;
456 }
457 SkScalar cmp = fDx * rh.fDy - rh.fDx * fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000458 if (cmp) {
459 return cmp < 0;
460 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000461 SkScalar dy = fDy + fDDy;
462 SkScalar rdy = rh.fDy + rh.fDDy;
463 if ((dy < 0) ^ (rdy < 0)) {
464 return dy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000465 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000466 SkScalar dx = fDx + fDDx;
467 SkScalar rdx = rh.fDx + rh.fDDx;
468 if (dy == 0 && rdy == 0 && dx != rdx) {
469 return dx < rdx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000470 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000471 cmp = dx * rdy - rdx * dy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000472 if (cmp) {
473 return cmp < 0;
474 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000475 dy += fDDDy;
476 rdy += rh.fDDDy;
477 if ((dy < 0) ^ (rdy < 0)) {
478 return dy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000479 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000480 dx += fDDDx;
481 rdx += rh.fDDDx;
482 if (dy == 0 && rdy == 0 && dx != rdx) {
483 return dx < rdx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000484 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000485 return dx * rdy < rdx * dy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000486 }
caryclark@google.com47580692012-07-23 12:14:49 +0000487
488 double dx() const {
489 return fDx;
490 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000491
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000492 double dy() const {
493 return fDy;
494 }
495
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000496 int end() const {
497 return fEnd;
498 }
499
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000500 bool isHorizontal() const {
501 return fDy == 0 && fDDy == 0 && fDDDy == 0;
502 }
503
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000504 // since all angles share a point, this needs to know which point
505 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
506 // practically, this should only be called by addAngle
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000507 void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000508 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000509 SkASSERT(start != end);
510 fSegment = segment;
511 fStart = start;
512 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000513 fDx = pts[1].fX - pts[0].fX; // b - a
514 fDy = pts[1].fY - pts[0].fY;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000515 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000516 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000517 return;
518 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000519 fDDx = pts[2].fX - pts[1].fX - fDx; // a - 2b + c
520 fDDy = pts[2].fY - pts[1].fY - fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000521 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000522 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000523 return;
524 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000525 fDDDx = pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX;
526 fDDDy = pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY;
527 }
528
529 // noncoincident quads/cubics may have the same initial angle
530 // as lines, so must sort by derivatives as well
531 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000532 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000533 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000534 fSegment = segment;
535 fStart = start;
536 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000537 fDx = pts[1].fX - pts[0].fX; // b - a
538 fDy = pts[1].fY - pts[0].fY;
539 if (verb == SkPath::kLine_Verb) {
540 fDDx = fDDy = fDDDx = fDDDy = 0;
541 return;
542 }
543 if (verb == SkPath::kQuad_Verb) {
544 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
545 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
546 int larger = std::max(abs(uplsX), abs(uplsY));
547 int shift = 0;
548 double flatT;
549 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
550 LineParameters implicitLine;
551 _Line tangent = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}};
552 implicitLine.lineEndPoints(tangent);
553 implicitLine.normalize();
554 while (larger > UlpsEpsilon * 1024) {
555 larger >>= 2;
556 ++shift;
557 flatT = 0.5 / (1 << shift);
558 QuadXYAtT(pts, flatT, &ddPt);
559 _Point _pt = {ddPt.fX, ddPt.fY};
560 double distance = implicitLine.pointDistance(_pt);
561 if (approximately_zero(distance)) {
562 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
563 break;
564 }
565 }
566 flatT = 0.5 / (1 << shift);
567 QuadXYAtT(pts, flatT, &ddPt);
568 fDDx = ddPt.fX - pts[0].fX;
569 fDDy = ddPt.fY - pts[0].fY;
570 SkASSERT(fDDx != 0 || fDDy != 0);
571 fDDDx = fDDDy = 0;
572 return;
573 }
574 SkASSERT(0); // FIXME: add cubic case
575 }
576
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000577 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000578 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000579 }
580
581 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000582 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000583 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000584
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000585 int start() const {
586 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000587 }
588
589private:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000590 SkScalar fDx;
591 SkScalar fDy;
592 SkScalar fDDx;
593 SkScalar fDDy;
594 SkScalar fDDDx;
595 SkScalar fDDDy;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000596 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000597 int fStart;
598 int fEnd;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000599};
600
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000601static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
602 int angleCount = angles.count();
603 int angleIndex;
604 angleList.setReserve(angleCount);
605 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
606 *angleList.append() = &angles[angleIndex];
607 }
608 QSort<Angle>(angleList.begin(), angleList.end() - 1);
609}
610
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000611// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000612struct Bounds : public SkRect {
613 static bool Intersects(const Bounds& a, const Bounds& b) {
614 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
615 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
616 }
617
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000618 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
619 if (left < fLeft) {
620 fLeft = left;
621 }
622 if (top < fTop) {
623 fTop = top;
624 }
625 if (right > fRight) {
626 fRight = right;
627 }
628 if (bottom > fBottom) {
629 fBottom = bottom;
630 }
631 }
632
633 void add(const Bounds& toAdd) {
634 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
635 }
636
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000637 bool isEmpty() {
638 return fLeft > fRight || fTop > fBottom
639 || fLeft == fRight && fTop == fBottom
640 || isnan(fLeft) || isnan(fRight)
641 || isnan(fTop) || isnan(fBottom);
642 }
643
644 void setCubicBounds(const SkPoint a[4]) {
645 _Rect dRect;
646 Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
647 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
648 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000649 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
650 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000651 }
652
653 void setQuadBounds(const SkPoint a[3]) {
654 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
655 {a[2].fX, a[2].fY}};
656 _Rect dRect;
657 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000658 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
659 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000660 }
661};
662
caryclark@google.com2ddff932012-08-07 21:25:27 +0000663static bool useInnerWinding(int outerWinding, int innerWinding) {
664 SkASSERT(outerWinding != innerWinding);
665 int absOut = abs(outerWinding);
666 int absIn = abs(innerWinding);
667 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
668 if (outerWinding * innerWinding < 0) {
669#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000670 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000671 outerWinding, innerWinding, result ? "true" : "false");
672#endif
673 }
674 return result;
675}
676
caryclark@google.com15fa1382012-05-07 20:49:36 +0000677struct Span {
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000678 Segment* fOther;
caryclark@google.com27c449a2012-07-27 18:26:38 +0000679 mutable SkPoint fPt; // lazily computed as needed
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000680 double fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000681 double fOtherT; // value at fOther[fOtherIndex].fT
682 int fOtherIndex; // can't be used during intersection
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000683 int fWindSum; // accumulated from contours surrounding this one
684 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000685 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000686};
687
688class Segment {
689public:
690 Segment() {
691#if DEBUG_DUMP
692 fID = ++gSegmentID;
693#endif
694 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000695
caryclark@google.com9764cc62012-07-12 19:29:45 +0000696 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
697 if (activeAngleInner(index, done, angles)) {
698 return true;
699 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000700 double referenceT = fTs[index].fT;
701 int lesser = index;
702 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000703 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000704 return true;
705 }
706 }
707 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000708 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000709 return true;
710 }
711 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
712 return false;
713 }
714
caryclark@google.com9764cc62012-07-12 19:29:45 +0000715 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000716 Span* span = &fTs[index];
717 Segment* other = span->fOther;
718 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +0000719 return other->activeAngleInner(oIndex, done, angles);
720 }
721
722 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
723 int next = nextSpan(index, 1);
724 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000725 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000726 if (upSpan.fWindValue) {
727 addAngle(angles, index, next);
728 if (upSpan.fDone) {
729 done++;
730 } else if (upSpan.fWindSum != SK_MinS32) {
731 return true;
732 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000733 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000734 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000735 int prev = nextSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000736 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +0000737 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000738 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000739 if (downSpan.fWindValue) {
740 addAngle(angles, index, prev);
741 if (downSpan.fDone) {
742 done++;
743 } else if (downSpan.fWindSum != SK_MinS32) {
744 return true;
745 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000746 }
747 }
748 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000749 }
750
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000751 SkScalar activeTop() const {
752 SkASSERT(!done());
753 int count = fTs.count();
754 SkScalar result = SK_ScalarMax;
755 bool lastDone = true;
756 for (int index = 0; index < count; ++index) {
757 bool done = fTs[index].fDone;
758 if (!done || !lastDone) {
759 SkScalar y = yAtT(index);
760 if (result > y) {
761 result = y;
762 }
763 }
764 lastDone = done;
765 }
766 SkASSERT(result < SK_ScalarMax);
767 return result;
768 }
769
770 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000771 SkASSERT(start != end);
772 SkPoint edge[4];
773 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
774 Angle* angle = angles.append();
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000775 angle->set(edge, fVerb, this, start, end);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000776 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000777
caryclark@google.com2ddff932012-08-07 21:25:27 +0000778 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +0000779 double oEnd) {
780 int tIndex = -1;
781 int tCount = fTs.count();
782 int oIndex = -1;
783 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +0000784 do {
785 ++tIndex;
786 } while (tStart - fTs[tIndex].fT >= FLT_EPSILON && tIndex < tCount);
787 int tIndexStart = tIndex;
788 do {
789 ++oIndex;
790 } while (oStart - other.fTs[oIndex].fT >= FLT_EPSILON && oIndex < oCount);
791 int oIndexStart = oIndex;
792 double nextT;
793 do {
794 nextT = fTs[++tIndex].fT;
795 } while (nextT < 1 && nextT - tStart < FLT_EPSILON);
796 double oNextT;
797 do {
798 oNextT = other.fTs[++oIndex].fT;
799 } while (oNextT < 1 && oNextT - oStart < FLT_EPSILON);
800 // at this point, spans before and after are at:
801 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
802 // if tIndexStart == 0, no prior span
803 // if nextT == 1, no following span
804
805 // advance the span with zero winding
806 // if the following span exists (not past the end, non-zero winding)
807 // connect the two edges
808 if (!fTs[tIndexStart].fWindValue) {
809 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
810 #if DEBUG_CONCIDENT
811 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
812 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000813 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
814 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +0000815 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000816 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000817 }
818 if (nextT < 1 && fTs[tIndex].fWindValue) {
819 #if DEBUG_CONCIDENT
820 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
821 __FUNCTION__, fID, other.fID, tIndex,
822 fTs[tIndex].fT, xyAtT(tIndex).fX,
823 xyAtT(tIndex).fY);
824 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000825 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000826 }
827 } else {
828 SkASSERT(!other.fTs[oIndexStart].fWindValue);
829 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
830 #if DEBUG_CONCIDENT
831 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
832 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000833 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
834 other.xyAtT(oIndexStart).fY);
835 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +0000836 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +0000837 }
838 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
839 #if DEBUG_CONCIDENT
840 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
841 __FUNCTION__, fID, other.fID, oIndex,
842 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
843 other.xyAtT(oIndex).fY);
844 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
845 #endif
846 }
847 }
848 }
849
850 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
851 double oEnd) {
852 // walk this to outsideTs[0]
853 // walk other to outsideTs[1]
854 // if either is > 0, add a pointer to the other, copying adjacent winding
855 int tIndex = -1;
856 int oIndex = -1;
857 double tStart = outsideTs[0];
858 double oStart = outsideTs[1];
859 do {
860 ++tIndex;
861 } while (tStart - fTs[tIndex].fT >= FLT_EPSILON);
862 do {
863 ++oIndex;
864 } while (oStart - other.fTs[oIndex].fT >= FLT_EPSILON);
865 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +0000866 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000867 }
868 tStart = fTs[tIndex].fT;
869 oStart = other.fTs[oIndex].fT;
870 do {
871 double nextT;
872 do {
873 nextT = fTs[++tIndex].fT;
874 } while (nextT - tStart < FLT_EPSILON);
875 tStart = nextT;
876 do {
877 nextT = other.fTs[++oIndex].fT;
878 } while (nextT - oStart < FLT_EPSILON);
879 oStart = nextT;
880 if (tStart == 1 && oStart == 1) {
881 break;
882 }
caryclark@google.com2ddff932012-08-07 21:25:27 +0000883 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000884 } while (tStart < 1 && oStart < 1 && oEnd - oStart >= FLT_EPSILON);
885 }
886
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000887 void addCubic(const SkPoint pts[4]) {
888 init(pts, SkPath::kCubic_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000889 fBounds.setCubicBounds(pts);
890 }
891
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000892 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000893 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000894 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000895 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000896 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000897 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000898 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000899 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
900 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
901 if (fVerb > 1) {
902 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
903 }
904 if (fVerb > 2) {
905 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
906 }
907 SkDebugf("\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000908 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000909 switch (fVerb) {
910 case SkPath::kLine_Verb:
911 path.lineTo(edge[1].fX, edge[1].fY);
912 break;
913 case SkPath::kQuad_Verb:
914 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
915 break;
916 case SkPath::kCubic_Verb:
917 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
918 edge[3].fX, edge[3].fY);
919 break;
920 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000921 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000922 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000923 }
924
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000925 void addLine(const SkPoint pts[2]) {
926 init(pts, SkPath::kLine_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000927 fBounds.set(pts, 2);
928 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000929
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000930 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
931 const SkPoint& pt = xyAtT(tIndex);
932 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000933 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000934 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000935 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000936 path.moveTo(pt.fX, pt.fY);
937 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000938 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000939 }
940
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000941 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000942 void addOtherT(int index, double otherT, int otherIndex) {
943 Span& span = fTs[index];
944 span.fOtherT = otherT;
945 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000946 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000947
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000948 void addQuad(const SkPoint pts[3]) {
949 init(pts, SkPath::kQuad_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000950 fBounds.setQuadBounds(pts);
951 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000952
953 // Defer all coincident edge processing until
954 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000955
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000956// no need to be tricky; insert in normal T order
957// resolve overlapping ts when considering coincidence later
958
959 // add non-coincident intersection. Resulting edges are sorted in T.
960 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000961 // FIXME: in the pathological case where there is a ton of intercepts,
962 // binary search?
963 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000964 size_t tCount = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000965 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000966 // OPTIMIZATION: if there are three or more identical Ts, then
967 // the fourth and following could be further insertion-sorted so
968 // that all the edges are clockwise or counterclockwise.
969 // This could later limit segment tests to the two adjacent
970 // neighbors, although it doesn't help with determining which
971 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000972 if (newT < fTs[index].fT) {
973 insertedAt = index;
974 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000975 }
976 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000977 Span* span;
978 if (insertedAt >= 0) {
979 span = fTs.insert(insertedAt);
980 } else {
981 insertedAt = tCount;
982 span = fTs.append();
983 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000984 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000985 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +0000986 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000987 span->fWindSum = SK_MinS32;
988 span->fWindValue = 1;
989 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000990 ++fDoneSpans;
991 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000992 return insertedAt;
993 }
994
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000995 // set spans from start to end to decrement by one
996 // note this walks other backwards
997 // FIMXE: there's probably an edge case that can be constructed where
998 // two span in one segment are separated by float epsilon on one span but
999 // not the other, if one segment is very small. For this
1000 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001001 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001002 // sorted? It feels better in such a case to match the span's other span
1003 // pointer since both coincident segments must contain the same spans.
1004 void addTCancel(double startT, double endT, Segment& other,
1005 double oStartT, double oEndT) {
1006 SkASSERT(endT - startT >= FLT_EPSILON);
1007 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
1008 int index = 0;
1009 while (startT - fTs[index].fT >= FLT_EPSILON) {
1010 ++index;
1011 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001012 int oIndex = other.fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001013 while (other.fTs[--oIndex].fT - oEndT > -FLT_EPSILON)
1014 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001015 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001016 Span* test = &fTs[index];
1017 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001018 SkTDArray<double> outsideTs;
1019 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001020 do {
1021 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001022 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001023 double testT = test->fT;
1024 double oTestT = oTest->fT;
1025 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001026 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001027 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001028 decrementSpan(span);
1029 } else if (track && span->fT < 1 && oTestT < 1) {
1030 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001031 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001032 span = &fTs[++index];
1033 } while (span->fT - testT < FLT_EPSILON);
1034 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001035 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1036 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1037 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
1038 while (oSpan->fT > otherTMatchStart - FLT_EPSILON
1039 && otherTMatchEnd - FLT_EPSILON > oSpan->fT) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001040 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001041 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001042 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001043 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001044 other.decrementSpan(oSpan);
1045 } else if (track && oSpan->fT < 1 && testT < 1) {
1046 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001047 }
1048 if (!oIndex) {
1049 break;
1050 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001051 oSpan = &other.fTs[--oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001052 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001053 test = span;
1054 oTest = oSpan;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001055 } while (test->fT < endT - FLT_EPSILON);
caryclark@google.com59823f72012-08-09 18:17:47 +00001056 SkASSERT(!oIndex || oTest->fT < oStartT + FLT_EPSILON);
caryclark@google.com18063442012-07-25 12:05:18 +00001057 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001058 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001059 double tStart = outsideTs[0];
1060 double oStart = outsideTs[1];
1061 addCancelOutsides(tStart, oStart, other, oEndT);
1062 int count = outsideTs.count();
1063 if (count > 2) {
1064 double tStart = outsideTs[count - 2];
1065 double oStart = outsideTs[count - 1];
1066 addCancelOutsides(tStart, oStart, other, oEndT);
1067 }
caryclark@google.com18063442012-07-25 12:05:18 +00001068 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001069 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001070 double tStart = oOutsideTs[0];
1071 double oStart = oOutsideTs[1];
1072 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001073 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001074 }
1075
1076 // set spans from start to end to increment the greater by one and decrement
1077 // the lesser
caryclark@google.com24bec792012-08-20 12:43:57 +00001078 void addTCoincident(const int xorMask, double startT, double endT, Segment& other,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001079 double oStartT, double oEndT) {
1080 SkASSERT(endT - startT >= FLT_EPSILON);
1081 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
1082 int index = 0;
1083 while (startT - fTs[index].fT >= FLT_EPSILON) {
1084 ++index;
1085 }
1086 int oIndex = 0;
1087 while (oStartT - other.fTs[oIndex].fT >= FLT_EPSILON) {
1088 ++oIndex;
1089 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001090 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001091 Span* test = &fTs[index];
1092 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001093 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001094 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001095 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001096 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001097 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001098 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com24bec792012-08-20 12:43:57 +00001099 bool winding = xorMask < 0;
1100 bool decrementThis = (test->fWindValue < oTest->fWindValue) & winding;
1101 bool decrementOther = (test->fWindValue >= oTest->fWindValue) & winding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001102 Span* end = test;
1103 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001104 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001105 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001106 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001107 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001108 if (transfer) {
1109 if (decrementOther) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001110 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001111 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001112 #endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001113 ++(end->fWindValue);
caryclark@google.com18063442012-07-25 12:05:18 +00001114 } else if (decrementSpan(end)) {
1115 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001116 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001117 } else if (oTest->fWindValue) {
1118 SkASSERT(!decrementOther);
1119 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1120 TrackOutside(xOutsideTs, end->fT, oStartT);
1121 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001122 }
1123 end = &fTs[++index];
1124 } while (end->fT - test->fT < FLT_EPSILON);
caryclark@google.com59823f72012-08-09 18:17:47 +00001125 // because of the order in which coincidences are resolved, this and other
1126 // may not have the same intermediate points. Compute the corresponding
1127 // intermediate T values (using this as the master, other as the follower)
1128 // and walk other conditionally -- hoping that it catches up in the end
1129 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001130 Span* oEnd = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001131 while (oEnd->fT < oEndT - FLT_EPSILON && oEnd->fT - otherTMatch < FLT_EPSILON) {
caryclark@google.comb9738012012-07-03 19:53:30 +00001132 if (transfer) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001133 if (decrementThis) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001134 #ifdef SK_DEBUG
1135 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1136 #endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001137 ++(oEnd->fWindValue);
caryclark@google.com18063442012-07-25 12:05:18 +00001138 } else if (other.decrementSpan(oEnd)) {
1139 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001140 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001141 } else if (test->fWindValue) {
1142 SkASSERT(!decrementOther);
1143 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1144 SkASSERT(0); // track for later?
1145 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001146 }
1147 oEnd = &other.fTs[++oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001148 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001149 test = end;
1150 oTest = oEnd;
1151 } while (test->fT < endT - FLT_EPSILON);
1152 SkASSERT(oTest->fT < oEndT + FLT_EPSILON);
1153 SkASSERT(oTest->fT > oEndT - FLT_EPSILON);
caryclark@google.comcc905052012-07-25 20:59:42 +00001154 if (!done()) {
1155 if (outsideTs.count()) {
1156 addCoinOutsides(outsideTs, other, oEndT);
1157 }
1158 if (xOutsideTs.count()) {
1159 addCoinOutsides(xOutsideTs, other, oEndT);
1160 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001161 }
1162 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001163 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001164 }
1165 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001166
caryclark@google.comcc905052012-07-25 20:59:42 +00001167 // FIXME: this doesn't prevent the same span from being added twice
1168 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001169 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001170 int tCount = fTs.count();
1171 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1172 const Span& span = fTs[tIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00001173 if (span.fT - t >= FLT_EPSILON) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001174 break;
1175 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001176 if (span.fT - t < FLT_EPSILON && span.fOther == &other && span.fOtherT == otherT) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001177#if DEBUG_ADD_T_PAIR
1178 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1179 __FUNCTION__, fID, t, other.fID, otherT);
1180#endif
1181 return;
1182 }
1183 }
caryclark@google.com47580692012-07-23 12:14:49 +00001184#if DEBUG_ADD_T_PAIR
1185 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1186 __FUNCTION__, fID, t, other.fID, otherT);
1187#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001188 int insertedAt = addT(t, &other);
1189 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001190 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001191 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001192 matchWindingValue(insertedAt, t, borrowWind);
1193 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001194 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00001195
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001196 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001197 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001198 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1199 addAngle(angles, end, start);
1200 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001201 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001202 int step = SkSign32(end - start);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001203 int tIndex = nextSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001204 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001205 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001206 }
1207 }
caryclark@google.com47580692012-07-23 12:14:49 +00001208
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001209 const Bounds& bounds() const {
1210 return fBounds;
1211 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001212
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001213 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001214 double referenceT = fTs[index].fT;
1215 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001216 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001217 buildAnglesInner(lesser, angles);
1218 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001219 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001220 buildAnglesInner(index, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001221 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001222 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001223
1224 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1225 Span* span = &fTs[index];
1226 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001227 // if there is only one live crossing, and no coincidence, continue
1228 // in the same direction
1229 // if there is coincidence, the only choice may be to reverse direction
1230 // find edge on either side of intersection
1231 int oIndex = span->fOtherIndex;
1232 // if done == -1, prior span has already been processed
1233 int step = 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001234 int next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001235 if (next < 0) {
1236 step = -step;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001237 next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001238 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001239 // add candidate into and away from junction
1240 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001241 }
1242
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001243 bool cancels(const Segment& other) const {
caryclark@google.comb9738012012-07-03 19:53:30 +00001244 SkASSERT(fVerb == SkPath::kLine_Verb);
1245 SkASSERT(other.fVerb == SkPath::kLine_Verb);
1246 SkPoint dxy = fPts[0] - fPts[1];
1247 SkPoint odxy = other.fPts[0] - other.fPts[1];
1248 return dxy.fX * odxy.fX < 0 || dxy.fY * odxy.fY < 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001249 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001250
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001251 // figure out if the segment's ascending T goes clockwise or not
1252 // not enough context to write this as shown
1253 // instead, add all segments meeting at the top
1254 // sort them using buildAngleList
1255 // find the first in the sort
1256 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001257 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001258 SkASSERT(0); // incomplete
1259 return false;
1260 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001261
1262 int computeSum(int startIndex, int endIndex) {
1263 SkTDArray<Angle> angles;
1264 addTwoAngles(startIndex, endIndex, angles);
1265 buildAngles(endIndex, angles);
1266 SkTDArray<Angle*> sorted;
1267 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001268#if DEBUG_SORT
1269 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
1270#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001271 int angleCount = angles.count();
1272 const Angle* angle;
1273 const Segment* base;
1274 int winding;
1275 int firstIndex = 0;
1276 do {
1277 angle = sorted[firstIndex];
1278 base = angle->segment();
1279 winding = base->windSum(angle);
1280 if (winding != SK_MinS32) {
1281 break;
1282 }
1283 if (++firstIndex == angleCount) {
1284 return SK_MinS32;
1285 }
1286 } while (true);
1287 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001288 int spanWinding = base->spanSign(angle);
1289 bool inner = useInnerWinding(winding + spanWinding, winding);
1290 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001291 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001292 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001293 inner ? winding + spanWinding : winding);
1294 #endif
1295 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001296 winding += spanWinding;
1297 }
1298 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001299 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001300 #endif
1301 int nextIndex = firstIndex + 1;
1302 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001303 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001304 do {
1305 if (nextIndex == angleCount) {
1306 nextIndex = 0;
1307 }
1308 angle = sorted[nextIndex];
1309 Segment* segment = angle->segment();
1310 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001311 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001312 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001313 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001314 maxWinding = winding;
1315 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001316 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001317 }
1318 } while (++nextIndex != lastIndex);
1319 return windSum(SkMin32(startIndex, endIndex));
1320 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001321
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001322 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001323 int bestT = -1;
1324 SkScalar top = bounds().fTop;
1325 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001326 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001327 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001328 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001329 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001330 if (fTs[start].fWindValue == 0) {
1331 continue;
1332 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001333 SkPoint edge[4];
1334 // OPTIMIZE: wrap this so that if start==0 end==fTCount-1 we can
1335 // work with the original data directly
caryclark@google.com24bec792012-08-20 12:43:57 +00001336 double startT = fTs[start].fT;
1337 double endT = fTs[end].fT;
1338 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001339 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001340 Intersections intersections;
1341 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1342 false, intersections);
1343 if (pts == 0) {
1344 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001345 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001346 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1347 // if the intersection is edge on, wait for another one
1348 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001349 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001350 SkASSERT(pts == 1); // FIXME: more code required to disambiguate
1351 SkPoint pt;
1352 double foundT = intersections.fT[0][0];
caryclark@google.com24bec792012-08-20 12:43:57 +00001353 double testT = startT + (endT - startT) * foundT;
1354 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
caryclark@google.com59823f72012-08-09 18:17:47 +00001355 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001356 bestY = pt.fY;
1357 bestT = foundT < 1 ? start : end;
caryclark@google.com24bec792012-08-20 12:43:57 +00001358 hitT = testT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001359 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001360 } while (fTs[end].fT != 1);
1361 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001362 }
caryclark@google.com18063442012-07-25 12:05:18 +00001363
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001364 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1365 // if a segment is connected to this one, consider it crossing
1366 int tIndex;
1367 if (fPts[0].fX == basePt.fX) {
1368 tIndex = 0;
1369 do {
1370 const Span& sSpan = fTs[tIndex];
1371 const Segment* sOther = sSpan.fOther;
1372 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1373 continue;
1374 }
1375 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1376 : sOther->fBounds.fRight > basePt.fX) {
1377 return true;
1378 }
1379 } while (fTs[++tIndex].fT == 0);
1380 }
1381 if (fPts[fVerb].fX == basePt.fX) {
1382 tIndex = fTs.count() - 1;
1383 do {
1384 const Span& eSpan = fTs[tIndex];
1385 const Segment* eOther = eSpan.fOther;
1386 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1387 continue;
1388 }
1389 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1390 : eOther->fBounds.fRight > basePt.fX) {
1391 return true;
1392 }
1393 } while (fTs[--tIndex].fT == 1);
1394 }
1395 return false;
1396 }
1397
caryclark@google.com18063442012-07-25 12:05:18 +00001398 bool decrementSpan(Span* span) {
1399 SkASSERT(span->fWindValue > 0);
1400 if (--(span->fWindValue) == 0) {
1401 span->fDone = true;
1402 ++fDoneSpans;
1403 return true;
1404 }
1405 return false;
1406 }
1407
caryclark@google.com15fa1382012-05-07 20:49:36 +00001408 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001409 SkASSERT(fDoneSpans <= fTs.count());
1410 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001411 }
1412
caryclark@google.com47580692012-07-23 12:14:49 +00001413 bool done(const Angle& angle) const {
1414 int start = angle.start();
1415 int end = angle.end();
1416 const Span& mSpan = fTs[SkMin32(start, end)];
1417 return mSpan.fDone;
1418 }
1419
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001420 // so the span needs to contain the pairing info found here
1421 // this should include the winding computed for the edge, and
1422 // what edge it connects to, and whether it is discarded
1423 // (maybe discarded == abs(winding) > 1) ?
1424 // only need derivatives for duration of sorting, add a new struct
1425 // for pairings, remove extra spans that have zero length and
1426 // reference an unused other
1427 // for coincident, the last span on the other may be marked done
1428 // (always?)
1429
1430 // if loop is exhausted, contour may be closed.
1431 // FIXME: pass in close point so we can check for closure
1432
1433 // given a segment, and a sense of where 'inside' is, return the next
1434 // segment. If this segment has an intersection, or ends in multiple
1435 // segments, find the mate that continues the outside.
1436 // note that if there are multiples, but no coincidence, we can limit
1437 // choices to connections in the correct direction
1438
1439 // mark found segments as done
1440
caryclark@google.com15fa1382012-05-07 20:49:36 +00001441 // start is the index of the beginning T of this edge
1442 // it is guaranteed to have an end which describes a non-zero length (?)
1443 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00001444 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
1445 int& nextStart, int& nextEnd, int& winding, int& spanWinding) {
1446 const int startIndex = nextStart;
1447 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001448 int outerWinding = winding;
1449 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00001450 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001451 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1452 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00001453 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001454 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001455 outerWinding = innerWinding;
1456 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001457 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001458 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001459 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1460 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001461 int step = SkSign32(endIndex - startIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001462 int end = nextSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001463 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001464 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001465 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001466 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001467 // mark the smaller of startIndex, endIndex done, and all adjacent
1468 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001469 #if DEBUG_WINDING
1470 SkDebugf("%s simple\n", __FUNCTION__);
1471 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001472 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001473 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001474 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00001475 double startT = other->fTs[nextStart].fT;
1476 nextEnd = nextStart;
1477 do {
1478 nextEnd += step;
1479 } while (fabs(startT - other->fTs[nextEnd].fT) < FLT_EPSILON);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001480 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001481 return other;
1482 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001483 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001484 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001485 SkASSERT(startIndex - endIndex != 0);
1486 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001487 addTwoAngles(startIndex, end, angles);
1488 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001489 SkTDArray<Angle*> sorted;
1490 sortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001491 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001492 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001493 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00001494 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001495 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001496 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001497 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001498 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001499 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00001500 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001501 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001502 int nextIndex = firstIndex + 1;
1503 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1504 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00001505 // FIXME: found done logic probably fails if there are more than 4
1506 // sorted angles. It should bias towards the first and last undone
1507 // edges -- but not sure that it won't choose a middle (incorrect)
1508 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00001509 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00001510 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001511 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00001512 bool altFlipped = false;
1513 bool foundFlipped = false;
1514 int foundMax = SK_MinS32;
1515 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001516 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00001517 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001518 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001519 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001520 nextIndex = 0;
1521 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001522 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00001523 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00001524 if (sumWinding) {
1525 lastNonZeroSum = sumWinding;
1526 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00001527 nextSegment = nextAngle->segment();
caryclark@google.com2ddff932012-08-07 21:25:27 +00001528 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00001529 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001530 #if DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00001531 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00001532 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
1533 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001534 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00001535 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00001536 if (!active) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001537 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001538 // FIXME: seems like a bug that this isn't calling userInnerWinding
caryclark@google.com47580692012-07-23 12:14:49 +00001539 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00001540 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001541 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001542 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001543 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00001544 return NULL;
1545 }
caryclark@google.com47580692012-07-23 12:14:49 +00001546 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001547 foundAngle = nextAngle;
caryclark@google.com47580692012-07-23 12:14:49 +00001548 foundDone = nextSegment->done(*nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00001549 foundFlipped = altFlipped;
1550 foundMax = maxWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001551 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001552 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001553 }
caryclark@google.com24bec792012-08-20 12:43:57 +00001554 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00001555 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001556 if (foundAngle && foundDone2) {
1557 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001558 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00001559 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00001560 foundAngle = nextAngle;
caryclark@google.com24bec792012-08-20 12:43:57 +00001561 foundDone2 = nextSegment->done(*nextAngle);
1562 foundFlipped = altFlipped;
1563 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00001564 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001565 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001566 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001567 }
1568 // if the winding is non-zero, nextAngle does not connect to
1569 // current chain. If we haven't done so already, mark the angle
1570 // as done, record the winding value, and mark connected unambiguous
1571 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001572 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001573 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00001574 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001575 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001576 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001577 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001578 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001579 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00001580 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001581 }
1582 if (last) {
1583 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001584 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001585 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001586 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00001587 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001588 if (!foundAngle) {
1589 return NULL;
1590 }
1591 nextStart = foundAngle->start();
1592 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00001593 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00001594 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001595 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
1596 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00001597 if (winding) {
1598 #if DEBUG_WINDING
1599 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
1600 if (foundSum == SK_MinS32) {
1601 SkDebugf("?");
1602 } else {
1603 SkDebugf("%d", foundSum);
1604 }
1605 SkDebugf(" foundMax=");
1606 if (foundMax == SK_MinS32) {
1607 SkDebugf("?");
1608 } else {
1609 SkDebugf("%d", foundMax);
1610 }
1611 SkDebugf("\n");
1612 #endif
1613 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001614 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00001615 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001616 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00001617 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00001618 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001619 }
caryclark@google.com24bec792012-08-20 12:43:57 +00001620
1621 Segment* findNextXor(int& nextStart, int& nextEnd) {
1622 const int startIndex = nextStart;
1623 const int endIndex = nextEnd;
1624 SkASSERT(startIndex != endIndex);
1625 int count = fTs.count();
1626 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1627 : startIndex > 0);
1628 int step = SkSign32(endIndex - startIndex);
1629 int end = nextSpan(startIndex, step);
1630 SkASSERT(end >= 0);
1631 Span* endSpan = &fTs[end];
1632 Segment* other;
1633 markDone(SkMin32(startIndex, endIndex), 1);
1634 if (isSimple(end)) {
1635 #if DEBUG_WINDING
1636 SkDebugf("%s simple\n", __FUNCTION__);
1637 #endif
1638 other = endSpan->fOther;
1639 nextStart = endSpan->fOtherIndex;
1640 double startT = other->fTs[nextStart].fT;
1641 SkDEBUGCODE(bool firstLoop = true;)
1642 if ((startT < FLT_EPSILON && step < 0)
1643 || (startT > 1 - FLT_EPSILON && step > 0)) {
1644 step = -step;
1645 SkDEBUGCODE(firstLoop = false;)
1646 }
1647 do {
1648 nextEnd = nextStart;
1649 do {
1650 nextEnd += step;
1651 } while (fabs(startT - other->fTs[nextEnd].fT) < FLT_EPSILON);
1652 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
1653 break;
1654 }
caryclark@google.com03f97062012-08-21 13:13:52 +00001655 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00001656 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00001657 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00001658 SkDEBUGCODE(firstLoop = false;)
1659 step = -step;
1660 } while (true);
1661 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1662 return other;
1663 }
1664 SkTDArray<Angle> angles;
1665 SkASSERT(startIndex - endIndex != 0);
1666 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1667 addTwoAngles(startIndex, end, angles);
1668 buildAngles(end, angles);
1669 SkTDArray<Angle*> sorted;
1670 sortAngles(angles, sorted);
1671 int angleCount = angles.count();
1672 int firstIndex = findStartingEdge(sorted, startIndex, end);
1673 SkASSERT(firstIndex >= 0);
1674 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001675 debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00001676 #endif
1677 SkASSERT(sorted[firstIndex]->segment() == this);
1678 int nextIndex = firstIndex + 1;
1679 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1680 const Angle* nextAngle;
1681 Segment* nextSegment;
1682 do {
1683 if (nextIndex == angleCount) {
1684 nextIndex = 0;
1685 }
1686 nextAngle = sorted[nextIndex];
1687 nextSegment = nextAngle->segment();
1688 if (!nextSegment->done(*nextAngle)) {
1689 break;
1690 }
1691 if (++nextIndex == lastIndex) {
1692 return NULL;
1693 }
1694 } while (true);
1695 nextStart = nextAngle->start();
1696 nextEnd = nextAngle->end();
1697 return nextSegment;
1698 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001699
1700 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
1701 int angleCount = sorted.count();
1702 int firstIndex = -1;
1703 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
1704 const Angle* angle = sorted[angleIndex];
1705 if (angle->segment() == this && angle->start() == end &&
1706 angle->end() == start) {
1707 firstIndex = angleIndex;
1708 break;
1709 }
1710 }
1711 return firstIndex;
1712 }
1713
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001714 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001715 void findTooCloseToCall(int /* winding */ ) { // FIXME: winding should be considered
caryclark@google.com15fa1382012-05-07 20:49:36 +00001716 int count = fTs.count();
1717 if (count < 3) { // require t=0, x, 1 at minimum
1718 return;
1719 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001720 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001721 int moCount;
1722 Span* match;
1723 Segment* mOther;
1724 do {
1725 match = &fTs[matchIndex];
1726 mOther = match->fOther;
1727 moCount = mOther->fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001728 if (moCount >= 3) {
1729 break;
1730 }
1731 if (++matchIndex >= count) {
1732 return;
1733 }
1734 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00001735 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001736 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001737 // look for a pair of nearby T values that map to the same (x,y) value
1738 // if found, see if the pair of other segments share a common point. If
1739 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00001740 for (int index = matchIndex + 1; index < count; ++index) {
1741 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001742 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001743 continue;
1744 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001745 Segment* tOther = test->fOther;
1746 int toCount = tOther->fTs.count();
1747 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001748 continue;
1749 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001750 const SkPoint* testPt = &xyAtT(test);
1751 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001752 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001753 moCount = toCount;
1754 match = test;
1755 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001756 matchPt = testPt;
1757 continue;
1758 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001759 int moStart = -1;
1760 int moEnd = -1;
1761 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001762 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001763 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001764 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001765 continue;
1766 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001767 if (moSpan.fOther == this) {
1768 if (moSpan.fOtherT == match->fT) {
1769 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001770 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001771 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001772 continue;
1773 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001774 if (moSpan.fOther == tOther) {
1775 SkASSERT(moEnd == -1);
1776 moEnd = moIndex;
1777 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001778 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001779 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001780 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001781 continue;
1782 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001783 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
1784 if (moStartT == moEndT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001785 continue;
1786 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001787 int toStart = -1;
1788 int toEnd = -1;
1789 double toStartT, toEndT;
1790 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
1791 Span& toSpan = tOther->fTs[toIndex];
1792 if (toSpan.fOther == this) {
1793 if (toSpan.fOtherT == test->fT) {
1794 toStart = toIndex;
1795 toStartT = toSpan.fT;
1796 }
1797 continue;
1798 }
1799 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
1800 SkASSERT(toEnd == -1);
1801 toEnd = toIndex;
1802 toEndT = toSpan.fT;
1803 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001804 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001805 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
1806 if (toStart <= 0 || toEnd <= 0) {
1807 continue;
1808 }
1809 if (toStartT == toEndT) {
1810 continue;
1811 }
1812 // test to see if the segment between there and here is linear
1813 if (!mOther->isLinear(moStart, moEnd)
1814 || !tOther->isLinear(toStart, toEnd)) {
1815 continue;
1816 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001817 // FIXME: defer implementation until the rest works
1818 // this may share code with regular coincident detection
1819 SkASSERT(0);
1820 #if 0
1821 if (flipped) {
1822 mOther->addTCancel(moStart, moEnd, tOther, tStart, tEnd);
1823 } else {
1824 mOther->addTCoincident(moStart, moEnd, tOther, tStart, tEnd);
1825 }
1826 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001827 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001828 }
1829
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001830 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
1831 // and use more concise logic like the old edge walker code?
1832 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001833 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001834 // iterate through T intersections and return topmost
1835 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001836 SkASSERT(!done());
1837 int firstT;
1838 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001839 SkPoint topPt;
1840 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001841 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001842 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001843 bool lastDone = true;
1844 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001845 const Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001846 if (!span.fDone || !lastDone) {
1847 const SkPoint& intercept = xyAtT(&span);
1848 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
1849 && topPt.fX > intercept.fX)) {
1850 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001851 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001852 } else if (topPt == intercept) {
1853 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001854 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001855 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001856 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001857 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001858 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001859 int step = 1;
1860 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001861 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001862 step = -1;
1863 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001864 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001865 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001866 // if the topmost T is not on end, or is three-way or more, find left
1867 // look for left-ness from tLeft to firstT (matching y of other)
1868 SkTDArray<Angle> angles;
1869 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001870 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001871 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001872 SkTDArray<Angle*> sorted;
1873 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001874 #if DEBUG_SORT
1875 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
1876 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001877 // skip edges that have already been processed
1878 firstT = -1;
1879 Segment* leftSegment;
1880 do {
1881 const Angle* angle = sorted[++firstT];
1882 leftSegment = angle->segment();
1883 tIndex = angle->end();
1884 endIndex = angle->start();
1885 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001886 return leftSegment;
1887 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001888
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001889 // FIXME: not crazy about this
1890 // when the intersections are performed, the other index is into an
1891 // incomplete array. as the array grows, the indices become incorrect
1892 // while the following fixes the indices up again, it isn't smart about
1893 // skipping segments whose indices are already correct
1894 // assuming we leave the code that wrote the index in the first place
1895 void fixOtherTIndex() {
1896 int iCount = fTs.count();
1897 for (int i = 0; i < iCount; ++i) {
1898 Span& iSpan = fTs[i];
1899 double oT = iSpan.fOtherT;
1900 Segment* other = iSpan.fOther;
1901 int oCount = other->fTs.count();
1902 for (int o = 0; o < oCount; ++o) {
1903 Span& oSpan = other->fTs[o];
1904 if (oT == oSpan.fT && this == oSpan.fOther) {
1905 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001906 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001907 }
1908 }
1909 }
1910 }
1911
caryclark@google.com495f8e42012-05-31 13:13:11 +00001912 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00001913 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001914 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001915 SkASSERT(end >= 0);
1916 if (multipleSpans(end)) {
1917 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001918 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001919 const Span& endSpan = fTs[end];
1920 Segment* other = endSpan.fOther;
1921 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001922 int otherEnd = other->nextSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00001923 Span* last = other->innerChaseDone(index, step, winding);
1924 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001925 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001926 }
1927
caryclark@google.com59823f72012-08-09 18:17:47 +00001928 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001929 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001930 SkASSERT(end >= 0);
1931 if (multipleSpans(end)) {
1932 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001933 }
1934 const Span& endSpan = fTs[end];
1935 Segment* other = endSpan.fOther;
1936 index = endSpan.fOtherIndex;
1937 int otherEnd = other->nextSpan(index, step);
1938 int min = SkMin32(index, otherEnd);
1939 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00001940 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001941 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001942 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001943 Span* last = other->innerChaseWinding(index, step, winding);
1944 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001945 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001946 }
1947
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001948 void init(const SkPoint pts[], SkPath::Verb verb) {
1949 fPts = pts;
1950 fVerb = verb;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001951 fDoneSpans = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001952 }
1953
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001954 bool intersected() const {
1955 return fTs.count() > 0;
1956 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00001957
1958 bool isConnected(int startIndex, int endIndex) const {
1959 return fTs[startIndex].fWindSum != SK_MinS32
1960 || fTs[endIndex].fWindSum != SK_MinS32;
1961 }
1962
caryclark@google.com15fa1382012-05-07 20:49:36 +00001963 bool isLinear(int start, int end) const {
1964 if (fVerb == SkPath::kLine_Verb) {
1965 return true;
1966 }
1967 if (fVerb == SkPath::kQuad_Verb) {
1968 SkPoint qPart[3];
1969 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
1970 return QuadIsLinear(qPart);
1971 } else {
1972 SkASSERT(fVerb == SkPath::kCubic_Verb);
1973 SkPoint cPart[4];
1974 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
1975 return CubicIsLinear(cPart);
1976 }
1977 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001978
1979 // OPTIMIZE: successive calls could start were the last leaves off
1980 // or calls could specialize to walk forwards or backwards
1981 bool isMissing(double startT) const {
1982 size_t tCount = fTs.count();
1983 for (size_t index = 0; index < tCount; ++index) {
1984 if (fabs(startT - fTs[index].fT) < FLT_EPSILON) {
1985 return false;
1986 }
1987 }
1988 return true;
1989 }
1990
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001991 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001992 int count = fTs.count();
1993 if (count == 2) {
1994 return true;
1995 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001996 double t = fTs[end].fT;
1997 if (t < FLT_EPSILON) {
1998 return fTs[1].fT >= FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001999 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002000 if (t > 1 - FLT_EPSILON) {
2001 return fTs[count - 2].fT <= 1 - FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002002 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002003 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002004 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002005
2006 bool isHorizontal() const {
2007 return fBounds.fTop == fBounds.fBottom;
2008 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002009
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002010 bool isVertical() const {
2011 return fBounds.fLeft == fBounds.fRight;
2012 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002013
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002014 SkScalar leftMost(int start, int end) const {
2015 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2016 }
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002017
caryclark@google.com495f8e42012-05-31 13:13:11 +00002018 // this span is excluded by the winding rule -- chase the ends
2019 // as long as they are unambiguous to mark connections as done
2020 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002021 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002022 int index = angle->start();
2023 int endIndex = angle->end();
2024 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002025 Span* last = innerChaseDone(index, step, winding);
2026 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002027 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002028 }
2029
caryclark@google.com59823f72012-08-09 18:17:47 +00002030 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002031 int index = angle->start();
2032 int endIndex = angle->end();
2033 int min = SkMin32(index, endIndex);
2034 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002035 Span* last = innerChaseWinding(index, step, winding);
2036 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002037 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002038 }
2039
caryclark@google.com495f8e42012-05-31 13:13:11 +00002040 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002041 // This may be called when the segment is already marked done. While this
2042 // wastes time, it shouldn't do any more than spin through the T spans.
2043 // OPTIMIZATION: abort on first done found (assuming that this code is
2044 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002045 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002046 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002047 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002048 double referenceT = fTs[index].fT;
2049 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002050 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002051 markOneDone(__FUNCTION__, lesser, winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002052 }
2053 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002054 markOneDone(__FUNCTION__, index, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002055 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
2056 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002057
2058 void markOneDone(const char* funName, int tIndex, int winding) {
2059 Span* span = markOneWinding(funName, tIndex, winding);
2060 if (!span) {
2061 return;
2062 }
2063 span->fDone = true;
2064 fDoneSpans++;
2065 }
2066
2067 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2068 Span& span = fTs[tIndex];
2069 if (span.fDone) {
2070 return NULL;
2071 }
2072 #if DEBUG_MARK_DONE
2073 debugShowNewWinding(funName, span, winding);
2074 #endif
2075 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002076 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002077 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002078 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002079 span.fWindSum = winding;
2080 return &span;
2081 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002082
caryclark@google.com59823f72012-08-09 18:17:47 +00002083 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002084 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002085 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002086 double referenceT = fTs[index].fT;
2087 int lesser = index;
2088 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002089 markOneWinding(__FUNCTION__, lesser, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002090 }
2091 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002092 markOneWinding(__FUNCTION__, index, winding);
2093 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002094 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002095
caryclark@google.com2ddff932012-08-07 21:25:27 +00002096 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002097 int nextDoorWind = SK_MaxS32;
2098 if (tIndex > 0) {
2099 const Span& below = fTs[tIndex - 1];
2100 if (t - below.fT < FLT_EPSILON) {
2101 nextDoorWind = below.fWindValue;
2102 }
2103 }
2104 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2105 const Span& above = fTs[tIndex + 1];
2106 if (above.fT - t < FLT_EPSILON) {
2107 nextDoorWind = above.fWindValue;
2108 }
2109 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002110 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2111 const Span& below = fTs[tIndex - 1];
2112 nextDoorWind = below.fWindValue;
2113 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002114 if (nextDoorWind != SK_MaxS32) {
2115 Span& newSpan = fTs[tIndex];
2116 newSpan.fWindValue = nextDoorWind;
2117 if (!nextDoorWind) {
2118 newSpan.fDone = true;
2119 ++fDoneSpans;
2120 }
2121 }
2122 }
2123
caryclark@google.com9764cc62012-07-12 19:29:45 +00002124 // return span if when chasing, two or more radiating spans are not done
2125 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2126 // candidate and the remaining spans have windValue == 0 (canceled by
2127 // coincidence). The coincident edges could either be removed altogether,
2128 // or this code could be more complicated in detecting this case. Worth it?
2129 bool multipleSpans(int end) const {
2130 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002131 }
2132
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002133 // This has callers for two different situations: one establishes the end
2134 // of the current span, and one establishes the beginning of the next span
2135 // (thus the name). When this is looking for the end of the current span,
2136 // coincidence is found when the beginning Ts contain -step and the end
2137 // contains step. When it is looking for the beginning of the next, the
2138 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002139 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002140 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002141 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002142 int count = fTs.count();
2143 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002144 while (step > 0 ? ++to < count : --to >= 0) {
2145 const Span& span = fTs[to];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002146 if ((step > 0 ? span.fT - fromSpan.fT : fromSpan.fT - span.fT) < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002147 continue;
2148 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002149 return to;
2150 }
2151 return -1;
2152 }
2153
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002154 const SkPoint* pts() const {
2155 return fPts;
2156 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002157
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002158 void reset() {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002159 init(NULL, (SkPath::Verb) -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002160 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2161 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002162 }
2163
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002164 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002165 const Span& span(int tIndex) const {
2166 return fTs[tIndex];
2167 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002168
2169 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002170 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue :
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002171 fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002172#if DEBUG_WIND_BUMP
2173 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2174#endif
2175 return result;
2176 }
2177
2178 int spanSign(const Angle* angle) const {
2179 SkASSERT(angle->segment() == this);
2180 return spanSign(angle->start(), angle->end());
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002181 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002182
2183 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002184 double t(int tIndex) const {
2185 return fTs[tIndex].fT;
2186 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002187
caryclark@google.com18063442012-07-25 12:05:18 +00002188 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
2189 double start) {
2190 int outCount = outsideTs.count();
2191 if (outCount == 0 || end - outsideTs[outCount - 2] >= FLT_EPSILON) {
2192 *outsideTs.append() = end;
2193 *outsideTs.append() = start;
2194 }
2195 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002196
2197 void undoneSpan(int& start, int& end) {
2198 size_t tCount = fTs.count();
2199 size_t index;
2200 for (index = 0; index < tCount; ++index) {
2201 if (!fTs[index].fDone) {
2202 break;
2203 }
2204 }
2205 SkASSERT(index < tCount - 1);
2206 start = index;
2207 double startT = fTs[index].fT;
2208 while (fTs[++index].fT - startT < FLT_EPSILON)
2209 SkASSERT(index < tCount);
2210 SkASSERT(index < tCount);
2211 end = index;
2212 }
caryclark@google.com18063442012-07-25 12:05:18 +00002213
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002214 void updatePts(const SkPoint pts[]) {
2215 fPts = pts;
2216 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002217
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002218 SkPath::Verb verb() const {
2219 return fVerb;
2220 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002221
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002222 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002223 return fTs[tIndex].fWindSum;
2224 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002225
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002226 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002227 int start = angle->start();
2228 int end = angle->end();
2229 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002230 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002231 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002232
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002233 int windValue(int tIndex) const {
2234 return fTs[tIndex].fWindValue;
2235 }
2236
2237 int windValue(const Angle* angle) const {
2238 int start = angle->start();
2239 int end = angle->end();
2240 int index = SkMin32(start, end);
2241 return windValue(index);
2242 }
2243
2244 SkScalar xAtT(const Span* span) const {
2245 return xyAtT(span).fX;
2246 }
2247
2248 const SkPoint& xyAtT(int index) const {
2249 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002250 }
2251
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002252 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002253 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002254 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002255 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002256 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002257 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002258 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002259 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002260 }
2261 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002262 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002263 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002264
2265 SkScalar yAtT(int index) const {
2266 return yAtT(&fTs[index]);
2267 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002268
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002269 SkScalar yAtT(const Span* span) const {
2270 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002271 }
2272
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002273#if DEBUG_DUMP
2274 void dump() const {
2275 const char className[] = "Segment";
2276 const int tab = 4;
2277 for (int i = 0; i < fTs.count(); ++i) {
2278 SkPoint out;
2279 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
2280 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002281 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002282 tab + sizeof(className), className, fID,
2283 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002284 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002285 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002286 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002287 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00002288 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002289 }
2290#endif
2291
caryclark@google.com47580692012-07-23 12:14:49 +00002292#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00002293 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002294 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00002295 for (int i = 0; i < fTs.count(); ++i) {
2296 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
2297 return;
2298 }
2299 }
2300 SkASSERT(0);
2301 }
2302#endif
2303
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002304#if DEBUG_DUMP
2305 int debugID() const {
2306 return fID;
2307 }
2308#endif
2309
caryclark@google.com24bec792012-08-20 12:43:57 +00002310#if DEBUG_WINDING
2311 void debugShowSums() const {
2312 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
2313 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
2314 for (int i = 0; i < fTs.count(); ++i) {
2315 const Span& span = fTs[i];
2316 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
2317 if (span.fWindSum == SK_MinS32) {
2318 SkDebugf("?");
2319 } else {
2320 SkDebugf("%d", span.fWindSum);
2321 }
2322 SkDebugf("]");
2323 }
2324 SkDebugf("\n");
2325 }
2326#endif
2327
caryclark@google.comcc905052012-07-25 20:59:42 +00002328#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002329 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00002330 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00002331 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00002332 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00002333 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
2334 }
2335 SkDebugf("\n");
2336 }
2337#endif
2338
caryclark@google.com027de222012-07-12 12:52:50 +00002339#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002340 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00002341 if (done()) {
2342 return;
2343 }
2344 for (int i = 0; i < fTs.count(); ++i) {
2345 if (fTs[i].fDone) {
2346 continue;
2347 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002348 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00002349 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2350 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2351 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2352 }
2353 const Span* span = &fTs[i];
caryclark@google.com0c803d02012-08-06 11:15:47 +00002354 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
2355 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00002356 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002357 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
2358 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
2359 if (fTs[i].fWindSum == SK_MinS32) {
2360 SkDebugf("?");
2361 } else {
2362 SkDebugf("%d", fTs[i].fWindSum);
2363 }
2364 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00002365 }
2366 }
2367#endif
2368
caryclark@google.com0c803d02012-08-06 11:15:47 +00002369#if DEBUG_MARK_DONE
2370 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
2371 const SkPoint& pt = xyAtT(&span);
2372 SkDebugf("%s id=%d", fun, fID);
2373 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2374 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2375 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2376 }
2377 SkDebugf(") t=%1.9g (%1.9g,%1.9g) newWindSum=%d windSum=",
2378 span.fT, pt.fX, pt.fY, winding);
2379 if (span.fWindSum == SK_MinS32) {
2380 SkDebugf("?");
2381 } else {
2382 SkDebugf("%d", span.fWindSum);
2383 }
2384 SkDebugf(" windValue=%d\n", span.fWindValue);
2385 }
2386#endif
2387
caryclark@google.com47580692012-07-23 12:14:49 +00002388#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002389 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002390 const int contourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002391 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00002392 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002393 int lastSum = contourWinding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002394 int windSum = lastSum - spanSign(angles[first]);
caryclark@google.com03f97062012-08-21 13:13:52 +00002395 SkDebugf("%s %s contourWinding=%d sign=%d\n", fun, __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +00002396 contourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00002397 int index = first;
2398 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00002399 do {
2400 const Angle& angle = *angles[index];
2401 const Segment& segment = *angle.segment();
2402 int start = angle.start();
2403 int end = angle.end();
2404 const Span& sSpan = segment.fTs[start];
2405 const Span& eSpan = segment.fTs[end];
2406 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002407 if (!firstTime) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002408 lastSum = windSum;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002409 windSum -= segment.spanSign(&angle);
caryclark@google.comafe56de2012-07-24 18:11:03 +00002410 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002411 SkDebugf("%s [%d] id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
caryclark@google.com47580692012-07-23 12:14:49 +00002412 " sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
caryclark@google.com03f97062012-08-21 13:13:52 +00002413 __FUNCTION__, index, segment.fID, kLVerbStr[segment.fVerb],
2414 start, segment.xAtT(&sSpan),
caryclark@google.com47580692012-07-23 12:14:49 +00002415 segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
2416 segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
caryclark@google.com2ddff932012-08-07 21:25:27 +00002417 lastSum, windSum, useInnerWinding(lastSum, windSum)
2418 ? windSum : lastSum, mSpan.fDone);
caryclark@google.com47580692012-07-23 12:14:49 +00002419 ++index;
2420 if (index == angles.count()) {
2421 index = 0;
2422 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002423 if (firstTime) {
2424 firstTime = false;
2425 }
caryclark@google.com47580692012-07-23 12:14:49 +00002426 } while (index != first);
2427 }
2428#endif
2429
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002430#if DEBUG_WINDING
2431 bool debugVerifyWinding(int start, int end, int winding) const {
2432 const Span& span = fTs[SkMin32(start, end)];
2433 int spanWinding = span.fWindSum;
2434 if (spanWinding == SK_MinS32) {
2435 return true;
2436 }
2437 int spanSign = SkSign32(start - end);
2438 int signedVal = spanSign * span.fWindValue;
2439 if (signedVal < 0) {
2440 spanWinding -= signedVal;
2441 }
2442 return span.fWindSum == winding;
2443 }
2444#endif
2445
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002446private:
2447 const SkPoint* fPts;
2448 SkPath::Verb fVerb;
2449 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002450 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00002451 int fDoneSpans; // quick check that segment is finished
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002452#if DEBUG_DUMP
2453 int fID;
2454#endif
2455};
2456
caryclark@google.comb9738012012-07-03 19:53:30 +00002457class Contour;
2458
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002459struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00002460 Contour* fContours[2];
2461 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002462 double fTs[2][2];
2463};
2464
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002465class Contour {
2466public:
2467 Contour() {
2468 reset();
2469#if DEBUG_DUMP
2470 fID = ++gContourID;
2471#endif
2472 }
2473
2474 bool operator<(const Contour& rh) const {
2475 return fBounds.fTop == rh.fBounds.fTop
2476 ? fBounds.fLeft < rh.fBounds.fLeft
2477 : fBounds.fTop < rh.fBounds.fTop;
2478 }
2479
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002480 void addCoincident(int index, Contour* other, int otherIndex,
2481 const Intersections& ts, bool swap) {
2482 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00002483 coincidence.fContours[0] = this;
2484 coincidence.fContours[1] = other;
2485 coincidence.fSegments[0] = index;
2486 coincidence.fSegments[1] = otherIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002487 coincidence.fTs[swap][0] = ts.fT[0][0];
2488 coincidence.fTs[swap][1] = ts.fT[0][1];
2489 coincidence.fTs[!swap][0] = ts.fT[1][0];
2490 coincidence.fTs[!swap][1] = ts.fT[1][1];
2491 }
2492
2493 void addCross(const Contour* crosser) {
2494#ifdef DEBUG_CROSS
2495 for (int index = 0; index < fCrosses.count(); ++index) {
2496 SkASSERT(fCrosses[index] != crosser);
2497 }
2498#endif
2499 *fCrosses.append() = crosser;
2500 }
2501
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002502 void addCubic(const SkPoint pts[4]) {
2503 fSegments.push_back().addCubic(pts);
2504 fContainsCurves = true;
2505 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002506
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002507 int addLine(const SkPoint pts[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002508 fSegments.push_back().addLine(pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002509 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002510 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002511
2512 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
2513 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
2514 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002515
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002516 int addQuad(const SkPoint pts[3]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002517 fSegments.push_back().addQuad(pts);
2518 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002519 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002520 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002521
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002522 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
2523 containsIntercepts();
2524 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
2525 }
2526
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002527 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002528 return fBounds;
2529 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002530
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002531 void complete() {
2532 setBounds();
2533 fContainsIntercepts = false;
2534 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002535
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002536 void containsIntercepts() {
2537 fContainsIntercepts = true;
2538 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002539
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002540 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
2541 int &tIndex, double& hitT) {
2542 int segmentCount = fSegments.count();
2543 const Segment* bestSegment = NULL;
2544 for (int test = 0; test < segmentCount; ++test) {
2545 Segment* testSegment = &fSegments[test];
2546 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002547 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002548 continue;
2549 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002550 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002551 continue;
2552 }
2553 if (bounds.fLeft > basePt.fX) {
2554 continue;
2555 }
2556 if (bounds.fRight < basePt.fX) {
2557 continue;
2558 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002559 if (bounds.fLeft == bounds.fRight) {
2560 continue;
2561 }
2562 #if 0
2563 bool leftHalf = bounds.fLeft == basePt.fX;
2564 bool rightHalf = bounds.fRight == basePt.fX;
2565 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
2566 basePt, leftHalf, rightHalf)) {
2567 continue;
2568 }
2569 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002570 double testHitT;
2571 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
2572 if (testT >= 0) {
2573 bestSegment = testSegment;
2574 tIndex = testT;
2575 hitT = testHitT;
2576 }
2577 }
2578 return bestSegment;
2579 }
2580
2581 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002582 for (int index = 0; index < fCrosses.count(); ++index) {
2583 if (fCrosses[index] == crosser) {
2584 return true;
2585 }
2586 }
2587 return false;
2588 }
2589
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002590 void findTooCloseToCall(int winding) {
2591 int segmentCount = fSegments.count();
2592 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
2593 fSegments[sIndex].findTooCloseToCall(winding);
2594 }
2595 }
2596
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002597 void fixOtherTIndex() {
2598 int segmentCount = fSegments.count();
2599 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
2600 fSegments[sIndex].fixOtherTIndex();
2601 }
2602 }
2603
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002604 void reset() {
2605 fSegments.reset();
2606 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002607 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002608 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002609
caryclark@google.com24bec792012-08-20 12:43:57 +00002610 void resolveCoincidence(int xorMask) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002611 int count = fCoincidences.count();
2612 for (int index = 0; index < count; ++index) {
2613 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00002614 Contour* thisContour = coincidence.fContours[0];
2615 Contour* otherContour = coincidence.fContours[1];
2616 int thisIndex = coincidence.fSegments[0];
2617 int otherIndex = coincidence.fSegments[1];
2618 Segment& thisOne = thisContour->fSegments[thisIndex];
2619 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00002620 #if DEBUG_CONCIDENT
2621 thisOne.debugShowTs();
2622 other.debugShowTs();
2623 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002624 double startT = coincidence.fTs[0][0];
2625 double endT = coincidence.fTs[0][1];
2626 if (startT > endT) {
2627 SkTSwap<double>(startT, endT);
2628 }
2629 SkASSERT(endT - startT >= FLT_EPSILON);
2630 double oStartT = coincidence.fTs[1][0];
2631 double oEndT = coincidence.fTs[1][1];
2632 if (oStartT > oEndT) {
2633 SkTSwap<double>(oStartT, oEndT);
2634 }
2635 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
caryclark@google.com24bec792012-08-20 12:43:57 +00002636 if (thisOne.cancels(other)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002637 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00002638 if (startT > 0 || oEndT < 1
2639 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
2640 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00002641 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002642 if (oStartT > 0 || endT < 1
2643 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
2644 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00002645 }
2646 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002647 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00002648 if (startT > 0 || oStartT > 0
2649 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002650 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00002651 }
caryclark@google.com200c2112012-08-03 15:05:04 +00002652 if (endT < 1 || oEndT < 1
2653 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002654 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00002655 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002656 thisOne.addTCoincident(xorMask, startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002657 }
caryclark@google.com47580692012-07-23 12:14:49 +00002658 #if DEBUG_CONCIDENT
2659 thisOne.debugShowTs();
2660 other.debugShowTs();
2661 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002662 }
2663 }
2664
2665 const SkTArray<Segment>& segments() {
2666 return fSegments;
2667 }
2668
caryclark@google.com15fa1382012-05-07 20:49:36 +00002669 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
2670 // we need to sort and walk edges in y, but that on the surface opens the
2671 // same can of worms as before. But then, this is a rough sort based on
2672 // segments' top, and not a true sort, so it could be ameniable to regular
2673 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002674 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002675 int segmentCount = fSegments.count();
2676 SkASSERT(segmentCount > 0);
2677 int best = -1;
2678 Segment* bestSegment = NULL;
2679 while (++best < segmentCount) {
2680 Segment* testSegment = &fSegments[best];
2681 if (testSegment->done()) {
2682 continue;
2683 }
2684 bestSegment = testSegment;
2685 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002686 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002687 if (!bestSegment) {
2688 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002689 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002690 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002691 for (int test = best + 1; test < segmentCount; ++test) {
2692 Segment* testSegment = &fSegments[test];
2693 if (testSegment->done()) {
2694 continue;
2695 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002696 if (testSegment->bounds().fTop > bestTop) {
2697 continue;
2698 }
2699 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002700 if (bestTop > testTop) {
2701 bestTop = testTop;
2702 bestSegment = testSegment;
2703 }
2704 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002705 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002706 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002707 }
2708
caryclark@google.com24bec792012-08-20 12:43:57 +00002709 Segment* undoneSegment(int& start, int& end) {
2710 int segmentCount = fSegments.count();
2711 for (int test = 0; test < segmentCount; ++test) {
2712 Segment* testSegment = &fSegments[test];
2713 if (testSegment->done()) {
2714 continue;
2715 }
2716 testSegment->undoneSpan(start, end);
2717 return testSegment;
2718 }
2719 return NULL;
2720 }
2721
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002722 int updateSegment(int index, const SkPoint* pts) {
2723 Segment& segment = fSegments[index];
2724 segment.updatePts(pts);
2725 return segment.verb() + 1;
2726 }
2727
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002728#if DEBUG_TEST
2729 SkTArray<Segment>& debugSegments() {
2730 return fSegments;
2731 }
2732#endif
2733
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002734#if DEBUG_DUMP
2735 void dump() {
2736 int i;
2737 const char className[] = "Contour";
2738 const int tab = 4;
2739 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
2740 for (i = 0; i < fSegments.count(); ++i) {
2741 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
2742 className, i);
2743 fSegments[i].dump();
2744 }
2745 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
2746 tab + sizeof(className), className,
2747 fBounds.fLeft, fBounds.fTop,
2748 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002749 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
2750 className, fContainsIntercepts);
2751 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
2752 className, fContainsCurves);
2753 }
2754#endif
2755
caryclark@google.com027de222012-07-12 12:52:50 +00002756#if DEBUG_ACTIVE_SPANS
2757 void debugShowActiveSpans() {
2758 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002759 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00002760 }
2761 }
2762#endif
2763
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002764protected:
2765 void setBounds() {
2766 int count = fSegments.count();
2767 if (count == 0) {
2768 SkDebugf("%s empty contour\n", __FUNCTION__);
2769 SkASSERT(0);
2770 // FIXME: delete empty contour?
2771 return;
2772 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002773 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002774 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002775 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002776 }
2777 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002778
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002779private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002780 SkTArray<Segment> fSegments;
2781 SkTDArray<Coincidence> fCoincidences;
2782 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002783 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002784 bool fContainsIntercepts;
2785 bool fContainsCurves;
2786#if DEBUG_DUMP
2787 int fID;
2788#endif
2789};
2790
2791class EdgeBuilder {
2792public:
2793
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002794EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002795 : fPath(path)
2796 , fCurrentContour(NULL)
2797 , fContours(contours)
2798{
2799#if DEBUG_DUMP
2800 gContourID = 0;
2801 gSegmentID = 0;
2802#endif
2803 walk();
2804}
2805
2806protected:
2807
2808void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002809 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002810 fCurrentContour->complete();
2811 fCurrentContour = NULL;
2812 }
2813}
2814
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002815void walk() {
2816 // FIXME:remove once we can access path pts directly
2817 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
2818 SkPoint pts[4];
2819 SkPath::Verb verb;
2820 do {
2821 verb = iter.next(pts);
2822 *fPathVerbs.append() = verb;
2823 if (verb == SkPath::kMove_Verb) {
2824 *fPathPts.append() = pts[0];
2825 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
2826 fPathPts.append(verb, &pts[1]);
2827 }
2828 } while (verb != SkPath::kDone_Verb);
2829 // FIXME: end of section to remove once path pts are accessed directly
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002830
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002831 SkPath::Verb reducedVerb;
2832 uint8_t* verbPtr = fPathVerbs.begin();
2833 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002834 const SkPoint* finalCurveStart = NULL;
2835 const SkPoint* finalCurveEnd = NULL;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002836 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
2837 switch (verb) {
2838 case SkPath::kMove_Verb:
2839 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002840 if (!fCurrentContour) {
2841 fCurrentContour = fContours.push_back_n(1);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002842 *fExtra.append() = -1; // start new contour
2843 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002844 finalCurveEnd = pointsPtr++;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002845 continue;
2846 case SkPath::kLine_Verb:
2847 // skip degenerate points
2848 if (pointsPtr[-1].fX != pointsPtr[0].fX
2849 || pointsPtr[-1].fY != pointsPtr[0].fY) {
2850 fCurrentContour->addLine(&pointsPtr[-1]);
2851 }
2852 break;
2853 case SkPath::kQuad_Verb:
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002854
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002855 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
2856 if (reducedVerb == 0) {
2857 break; // skip degenerate points
2858 }
2859 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002860 *fExtra.append() =
2861 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002862 break;
2863 }
2864 fCurrentContour->addQuad(&pointsPtr[-1]);
2865 break;
2866 case SkPath::kCubic_Verb:
2867 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
2868 if (reducedVerb == 0) {
2869 break; // skip degenerate points
2870 }
2871 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002872 *fExtra.append() =
2873 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002874 break;
2875 }
2876 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002877 *fExtra.append() =
2878 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002879 break;
2880 }
2881 fCurrentContour->addCubic(&pointsPtr[-1]);
2882 break;
2883 case SkPath::kClose_Verb:
2884 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002885 if (finalCurveStart && finalCurveEnd
2886 && *finalCurveStart != *finalCurveEnd) {
2887 *fReducePts.append() = *finalCurveStart;
2888 *fReducePts.append() = *finalCurveEnd;
2889 *fExtra.append() =
2890 fCurrentContour->addLine(fReducePts.end() - 2);
2891 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002892 complete();
2893 continue;
2894 default:
2895 SkDEBUGFAIL("bad verb");
2896 return;
2897 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002898 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002899 pointsPtr += verb;
2900 SkASSERT(fCurrentContour);
2901 }
2902 complete();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002903 if (fCurrentContour && !fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002904 fContours.pop_back();
2905 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002906 // correct pointers in contours since fReducePts may have moved as it grew
2907 int cIndex = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002908 int extraCount = fExtra.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002909 SkASSERT(extraCount == 0 || fExtra[0] == -1);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002910 int eIndex = 0;
2911 int rIndex = 0;
2912 while (++eIndex < extraCount) {
2913 int offset = fExtra[eIndex];
2914 if (offset < 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002915 ++cIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002916 continue;
2917 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002918 fCurrentContour = &fContours[cIndex];
2919 rIndex += fCurrentContour->updateSegment(offset - 1,
2920 &fReducePts[rIndex]);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002921 }
2922 fExtra.reset(); // we're done with this
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002923}
2924
2925private:
2926 const SkPath& fPath;
2927 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002928 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002929 Contour* fCurrentContour;
2930 SkTArray<Contour>& fContours;
2931 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002932 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002933};
2934
2935class Work {
2936public:
2937 enum SegmentType {
2938 kHorizontalLine_Segment = -1,
2939 kVerticalLine_Segment = 0,
2940 kLine_Segment = SkPath::kLine_Verb,
2941 kQuad_Segment = SkPath::kQuad_Verb,
2942 kCubic_Segment = SkPath::kCubic_Verb,
2943 };
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002944
2945 void addCoincident(Work& other, const Intersections& ts, bool swap) {
2946 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
2947 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002948
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002949 // FIXME: does it make sense to write otherIndex now if we're going to
2950 // fix it up later?
2951 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002952 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002953 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002954
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002955 // Avoid collapsing t values that are close to the same since
2956 // we walk ts to describe consecutive intersections. Since a pair of ts can
2957 // be nearly equal, any problems caused by this should be taken care
2958 // of later.
2959 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002960 int addT(double newT, const Work& other) {
2961 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002962 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002963
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002964 bool advance() {
2965 return ++fIndex < fLast;
2966 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002967
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002968 SkScalar bottom() const {
2969 return bounds().fBottom;
2970 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002971
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002972 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002973 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002974 }
2975
2976 const SkPoint* cubic() const {
2977 return fCubic;
2978 }
2979
2980 void init(Contour* contour) {
2981 fContour = contour;
2982 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002983 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002984 }
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002985
2986 bool isAdjacent(const Work& next) {
2987 return fContour == next.fContour && fIndex + 1 == next.fIndex;
2988 }
2989
2990 bool isFirstLast(const Work& next) {
2991 return fContour == next.fContour && fIndex == 0
2992 && next.fIndex == fLast - 1;
2993 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002994
2995 SkScalar left() const {
2996 return bounds().fLeft;
2997 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002998
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002999 void promoteToCubic() {
3000 fCubic[0] = pts()[0];
3001 fCubic[2] = pts()[1];
3002 fCubic[3] = pts()[2];
3003 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
3004 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
3005 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
3006 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
3007 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003008
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003009 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003010 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003011 }
3012
3013 SkScalar right() const {
3014 return bounds().fRight;
3015 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003016
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003017 ptrdiff_t segmentIndex() const {
3018 return fIndex;
3019 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003020
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003021 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003022 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003023 SegmentType type = (SegmentType) segment.verb();
3024 if (type != kLine_Segment) {
3025 return type;
3026 }
3027 if (segment.isHorizontal()) {
3028 return kHorizontalLine_Segment;
3029 }
3030 if (segment.isVertical()) {
3031 return kVerticalLine_Segment;
3032 }
3033 return kLine_Segment;
3034 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003035
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003036 bool startAfter(const Work& after) {
3037 fIndex = after.fIndex;
3038 return advance();
3039 }
3040
3041 SkScalar top() const {
3042 return bounds().fTop;
3043 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003044
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003045 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003046 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003047 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003048
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003049 SkScalar x() const {
3050 return bounds().fLeft;
3051 }
3052
3053 bool xFlipped() const {
3054 return x() != pts()[0].fX;
3055 }
3056
3057 SkScalar y() const {
3058 return bounds().fTop;
3059 }
3060
3061 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003062 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003063 }
3064
3065protected:
3066 Contour* fContour;
3067 SkPoint fCubic[4];
3068 int fIndex;
3069 int fLast;
3070};
3071
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003072#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003073static void debugShowLineIntersection(int pts, const Work& wt,
3074 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003075 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003076 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
3077 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
3078 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
3079 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003080 return;
3081 }
3082 SkPoint wtOutPt, wnOutPt;
3083 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3084 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003085 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003086 __FUNCTION__,
3087 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3088 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
3089 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003090 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003091 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003092 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003093 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3094 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
3095 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003096 SkDebugf(" wnTs[1]=%g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003097 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003098 SkDebugf("\n");
3099}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003100#else
3101static void debugShowLineIntersection(int , const Work& ,
3102 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003103}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003104#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003105
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003106static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003107
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003108 if (test != next) {
3109 if (test->bounds().fBottom < next->bounds().fTop) {
3110 return false;
3111 }
3112 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
3113 return true;
3114 }
3115 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003116 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003117 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003118 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003119 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003120 Work wn;
3121 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003122 if (test == next && !wn.startAfter(wt)) {
3123 continue;
3124 }
3125 do {
3126 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
3127 continue;
3128 }
3129 int pts;
3130 Intersections ts;
3131 bool swap = false;
3132 switch (wt.segmentType()) {
3133 case Work::kHorizontalLine_Segment:
3134 swap = true;
3135 switch (wn.segmentType()) {
3136 case Work::kHorizontalLine_Segment:
3137 case Work::kVerticalLine_Segment:
3138 case Work::kLine_Segment: {
3139 pts = HLineIntersect(wn.pts(), wt.left(),
3140 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003141 debugShowLineIntersection(pts, wt, wn,
3142 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003143 break;
3144 }
3145 case Work::kQuad_Segment: {
3146 pts = HQuadIntersect(wn.pts(), wt.left(),
3147 wt.right(), wt.y(), wt.xFlipped(), ts);
3148 break;
3149 }
3150 case Work::kCubic_Segment: {
3151 pts = HCubicIntersect(wn.pts(), wt.left(),
3152 wt.right(), wt.y(), wt.xFlipped(), ts);
3153 break;
3154 }
3155 default:
3156 SkASSERT(0);
3157 }
3158 break;
3159 case Work::kVerticalLine_Segment:
3160 swap = true;
3161 switch (wn.segmentType()) {
3162 case Work::kHorizontalLine_Segment:
3163 case Work::kVerticalLine_Segment:
3164 case Work::kLine_Segment: {
3165 pts = VLineIntersect(wn.pts(), wt.top(),
3166 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003167 debugShowLineIntersection(pts, wt, wn,
3168 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003169 break;
3170 }
3171 case Work::kQuad_Segment: {
3172 pts = VQuadIntersect(wn.pts(), wt.top(),
3173 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3174 break;
3175 }
3176 case Work::kCubic_Segment: {
3177 pts = VCubicIntersect(wn.pts(), wt.top(),
3178 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3179 break;
3180 }
3181 default:
3182 SkASSERT(0);
3183 }
3184 break;
3185 case Work::kLine_Segment:
3186 switch (wn.segmentType()) {
3187 case Work::kHorizontalLine_Segment:
3188 pts = HLineIntersect(wt.pts(), wn.left(),
3189 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003190 debugShowLineIntersection(pts, wt, wn,
3191 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003192 break;
3193 case Work::kVerticalLine_Segment:
3194 pts = VLineIntersect(wt.pts(), wn.top(),
3195 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003196 debugShowLineIntersection(pts, wt, wn,
3197 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003198 break;
3199 case Work::kLine_Segment: {
3200 pts = LineIntersect(wt.pts(), wn.pts(), ts);
3201 debugShowLineIntersection(pts, wt, wn,
3202 ts.fT[1], ts.fT[0]);
3203 break;
3204 }
3205 case Work::kQuad_Segment: {
3206 swap = true;
3207 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
3208 break;
3209 }
3210 case Work::kCubic_Segment: {
3211 swap = true;
3212 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
3213 break;
3214 }
3215 default:
3216 SkASSERT(0);
3217 }
3218 break;
3219 case Work::kQuad_Segment:
3220 switch (wn.segmentType()) {
3221 case Work::kHorizontalLine_Segment:
3222 pts = HQuadIntersect(wt.pts(), wn.left(),
3223 wn.right(), wn.y(), wn.xFlipped(), ts);
3224 break;
3225 case Work::kVerticalLine_Segment:
3226 pts = VQuadIntersect(wt.pts(), wn.top(),
3227 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3228 break;
3229 case Work::kLine_Segment: {
3230 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
3231 break;
3232 }
3233 case Work::kQuad_Segment: {
3234 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
3235 break;
3236 }
3237 case Work::kCubic_Segment: {
3238 wt.promoteToCubic();
3239 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
3240 break;
3241 }
3242 default:
3243 SkASSERT(0);
3244 }
3245 break;
3246 case Work::kCubic_Segment:
3247 switch (wn.segmentType()) {
3248 case Work::kHorizontalLine_Segment:
3249 pts = HCubicIntersect(wt.pts(), wn.left(),
3250 wn.right(), wn.y(), wn.xFlipped(), ts);
3251 break;
3252 case Work::kVerticalLine_Segment:
3253 pts = VCubicIntersect(wt.pts(), wn.top(),
3254 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3255 break;
3256 case Work::kLine_Segment: {
3257 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
3258 break;
3259 }
3260 case Work::kQuad_Segment: {
3261 wn.promoteToCubic();
3262 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
3263 break;
3264 }
3265 case Work::kCubic_Segment: {
3266 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
3267 break;
3268 }
3269 default:
3270 SkASSERT(0);
3271 }
3272 break;
3273 default:
3274 SkASSERT(0);
3275 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003276 if (!foundCommonContour && pts > 0) {
3277 test->addCross(next);
3278 next->addCross(test);
3279 foundCommonContour = true;
3280 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003281 // in addition to recording T values, record matching segment
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003282 if (pts == 2 && wn.segmentType() <= Work::kLine_Segment
3283 && wt.segmentType() <= Work::kLine_Segment) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003284 wt.addCoincident(wn, ts, swap);
3285 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003286 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003287 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003288 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
3289 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003290 int testTAt = wt.addT(ts.fT[swap][pt], wn);
3291 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003292 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
3293 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003294 }
3295 } while (wn.advance());
3296 } while (wt.advance());
3297 return true;
3298}
3299
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003300// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003301// see if coincidence is formed by clipping non-concident segments
caryclark@google.com24bec792012-08-20 12:43:57 +00003302static void coincidenceCheck(SkTDArray<Contour*>& contourList, int xorMask) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003303 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00003304 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003305 Contour* contour = contourList[cIndex];
caryclark@google.com24bec792012-08-20 12:43:57 +00003306 contour->findTooCloseToCall(xorMask);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003307 }
3308 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
3309 Contour* contour = contourList[cIndex];
caryclark@google.com24bec792012-08-20 12:43:57 +00003310 contour->resolveCoincidence(xorMask);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003311 }
3312}
3313
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003314// project a ray from the top of the contour up and see if it hits anything
3315// note: when we compute line intersections, we keep track of whether
3316// two contours touch, so we need only look at contours not touching this one.
3317// OPTIMIZATION: sort contourList vertically to avoid linear walk
3318static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003319 const Segment* current, int index, int endIndex) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003320 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003321 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003322 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00003323 const Segment* test = NULL;
3324 int tIndex;
3325 double tHit;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003326 // bool checkCrosses = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003327 for (int cTest = 0; cTest < contourCount; ++cTest) {
3328 Contour* contour = contourList[cTest];
3329 if (basePt.fY < contour->bounds().fTop) {
3330 continue;
3331 }
3332 if (bestY > contour->bounds().fBottom) {
3333 continue;
3334 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003335#if 0
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003336 // even though the contours crossed, if spans cancel through concidence,
3337 // the contours may be not have any span links to chase, and the current
3338 // segment may be isolated. Detect this by seeing if current has
3339 // uninitialized wind sums. If so, project a ray instead of relying on
3340 // previously found intersections.
3341 if (baseContour == contour) {
3342 continue;
3343 }
3344 if (checkCrosses && baseContour->crosses(contour)) {
3345 if (current->isConnected(index, endIndex)) {
3346 continue;
3347 }
3348 checkCrosses = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003349 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003350#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003351 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
3352 if (next) {
3353 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003354 }
caryclark@google.com47580692012-07-23 12:14:49 +00003355 }
3356 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00003357 return 0;
3358 }
3359 int winding, windValue;
3360 // If the ray hit the end of a span, we need to construct the wheel of
3361 // angles to find the span closest to the ray -- even if there are just
3362 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003363 const Angle* angle = NULL;
caryclark@google.come21cb182012-07-23 21:26:31 +00003364 if (fabs(tHit - test->t(tIndex)) < FLT_EPSILON) {
caryclark@google.com47580692012-07-23 12:14:49 +00003365 SkTDArray<Angle> angles;
3366 int end = test->nextSpan(tIndex, 1);
3367 if (end < 0) {
3368 end = test->nextSpan(tIndex, -1);
3369 }
3370 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00003371 SkASSERT(angles.count() > 0);
3372 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
3373#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00003374 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00003375#endif
3376 return 0;
3377 }
caryclark@google.com47580692012-07-23 12:14:49 +00003378 test->buildAngles(tIndex, angles);
3379 SkTDArray<Angle*> sorted;
3380 // OPTIMIZATION: call a sort that, if base point is the leftmost,
3381 // returns the first counterclockwise hour before 6 o'clock,
3382 // or if the base point is rightmost, returns the first clockwise
3383 // hour after 6 o'clock
3384 sortAngles(angles, sorted);
3385#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003386 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00003387#endif
3388 // walk the sorted angle fan to find the lowest angle
3389 // above the base point. Currently, the first angle in the sorted array
3390 // is 12 noon or an earlier hour (the next counterclockwise)
3391 int count = sorted.count();
3392 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003393 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00003394 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003395 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00003396 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00003397 angle = sorted[index];
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003398 if (baseMatches && angle->isHorizontal()) {
3399 continue;
3400 }
3401 double indexDx = angle->dx();
caryclark@google.com47580692012-07-23 12:14:49 +00003402 if (indexDx < 0) {
3403 left = index;
3404 } else if (indexDx > 0) {
3405 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00003406 int previous = index - 1;
3407 if (previous < 0) {
3408 previous = count - 1;
3409 }
3410 const Angle* prev = sorted[previous];
3411 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
3412#if DEBUG_SORT
3413 SkDebugf("%s use prev\n", __FUNCTION__);
3414#endif
3415 right = previous;
3416 }
caryclark@google.com47580692012-07-23 12:14:49 +00003417 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003418 } else {
3419 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003420 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003421 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003422 if (left < 0 && right < 0) {
3423 left = mid;
3424 }
caryclark@google.com47580692012-07-23 12:14:49 +00003425 SkASSERT(left >= 0 || right >= 0);
3426 if (left < 0) {
3427 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003428 } else if (left >= 0 && mid >= 0 && right >= 0
3429 && sorted[mid]->sign() == sorted[right]->sign()) {
3430 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003431 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003432 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00003433 test = angle->segment();
3434 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00003435 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003436 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00003437#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003438 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
3439 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00003440#endif
3441 } else {
3442 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00003443 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003444 windValue = test->windValue(tIndex);
3445#if DEBUG_WINDING
3446 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
3447 windValue);
3448#endif
3449 }
3450 // see if a + change in T results in a +/- change in X (compute x'(T))
3451 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
3452#if DEBUG_WINDING
3453 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
3454#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00003455 SkASSERT(dx != 0);
3456 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00003457 winding += dx > 0 ? -windValue : windValue;
3458#if DEBUG_WINDING
3459 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
3460#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003461 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003462 // start here;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003463 // we're broken because we find a vertical span
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003464 return winding;
3465}
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003466
3467// OPTIMIZATION: not crazy about linear search here to find top active y.
3468// seems like we should break down and do the sort, or maybe sort each
3469// contours' segments?
3470// Once the segment array is built, there's no reason I can think of not to
3471// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003472// FIXME: return the contour found to pass to inner contour check
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003473static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003474 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003475 int cIndex = 0;
3476 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003477 SkScalar bestY = SK_ScalarMax;
3478 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003479 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003480 contour = contourList[cIndex];
3481 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003482 } while (!topStart && ++cIndex < contourCount);
3483 if (!topStart) {
3484 return NULL;
3485 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003486 while (++cIndex < contourCount) {
3487 contour = contourList[cIndex];
3488 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003489 continue;
3490 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003491 SkScalar testY = SK_ScalarMax;
3492 Segment* test = contour->topSegment(testY);
3493 if (!test || bestY <= testY) {
3494 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003495 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003496 topStart = test;
3497 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003498 }
3499 return topStart;
3500}
3501
caryclark@google.com24bec792012-08-20 12:43:57 +00003502static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
3503 int contourCount = contourList.count();
3504 Segment* result;
3505 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
3506 Contour* contour = contourList[cIndex];
3507 result = contour->undoneSegment(start, end);
3508 if (result) {
3509 return result;
3510 }
3511 }
3512 return NULL;
3513}
3514
3515
3516
caryclark@google.come21cb182012-07-23 21:26:31 +00003517static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
3518 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003519 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00003520 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003521 const Span& backPtr = span->fOther->span(span->fOtherIndex);
3522 Segment* segment = backPtr.fOther;
3523 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003524 SkTDArray<Angle> angles;
3525 int done = 0;
3526 if (segment->activeAngle(tIndex, done, angles)) {
3527 Angle* last = angles.end() - 1;
3528 tIndex = last->start();
3529 endIndex = last->end();
3530 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003531 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00003532 if (done == angles.count()) {
3533 chase.pop(&span);
3534 continue;
3535 }
3536 SkTDArray<Angle*> sorted;
3537 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00003538#if DEBUG_SORT
3539 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
3540#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003541 // find first angle, initialize winding to computed fWindSum
3542 int firstIndex = -1;
3543 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003544 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003545 do {
3546 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003547 segment = angle->segment();
3548 winding = segment->windSum(angle);
3549 } while (winding == SK_MinS32);
3550 int spanWinding = segment->spanSign(angle->start(), angle->end());
3551 #if DEBUG_WINDING
3552 SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
3553 __FUNCTION__, winding, spanWinding, contourWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00003554 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003555 // turn swinding into contourWinding
3556 if (spanWinding * winding < 0) {
3557 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003558 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003559 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003560 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003561 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003562 // we care about first sign and whether wind sum indicates this
3563 // edge is inside or outside. Maybe need to pass span winding
3564 // or first winding or something into this function?
3565 // advance to first undone angle, then return it and winding
3566 // (to set whether edges are active or not)
3567 int nextIndex = firstIndex + 1;
3568 int angleCount = sorted.count();
3569 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003570 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00003571 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00003572 do {
3573 SkASSERT(nextIndex != firstIndex);
3574 if (nextIndex == angleCount) {
3575 nextIndex = 0;
3576 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003577 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00003578 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003579 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003580 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003581 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00003582 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
3583 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003584 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003585 tIndex = angle->start();
3586 endIndex = angle->end();
3587 int lesser = SkMin32(tIndex, endIndex);
3588 const Span& nextSpan = segment->span(lesser);
3589 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003590#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00003591 // FIXME: this be wrong. assign startWinding if edge is in
3592 // same direction. If the direction is opposite, winding to
3593 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00003594 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003595 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003596 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003597 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003598#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003599 break;
3600 }
3601 } while (++nextIndex != lastIndex);
3602 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003603 }
3604 return NULL;
3605}
3606
caryclark@google.com027de222012-07-12 12:52:50 +00003607#if DEBUG_ACTIVE_SPANS
3608static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
3609 for (int index = 0; index < contourList.count(); ++ index) {
3610 contourList[index]->debugShowActiveSpans();
3611 }
3612}
3613#endif
3614
caryclark@google.com27c449a2012-07-27 18:26:38 +00003615static bool windingIsActive(int winding, int spanWinding) {
3616 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
3617 && (!winding || !spanWinding || winding == -spanWinding);
3618}
3619
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003620// Each segment may have an inside or an outside. Segments contained within
3621// winding may have insides on either side, and form a contour that should be
3622// ignored. Segments that are coincident with opposing direction segments may
3623// have outsides on either side, and should also disappear.
3624// 'Normal' segments will have one inside and one outside. Subsequent connections
3625// when winding should follow the intersection direction. If more than one edge
3626// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003627 // since we start with leftmost top edge, we'll traverse through a
3628 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com24bec792012-08-20 12:43:57 +00003629static void bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003630 bool firstContour = true;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003631 do {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003632 Segment* topStart = findTopContour(contourList);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003633 if (!topStart) {
3634 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00003635 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003636 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00003637 // follow edges to intersection by changing the index by direction.
3638 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003639 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003640 int contourWinding;
3641 if (firstContour) {
3642 contourWinding = 0;
3643 firstContour = false;
3644 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003645 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003646 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00003647 if (sumWinding == SK_MinS32) {
3648 sumWinding = current->computeSum(index, endIndex);
3649 }
3650 if (sumWinding == SK_MinS32) {
3651 contourWinding = innerContourCheck(contourList, current,
3652 index, endIndex);
3653 } else {
3654 contourWinding = sumWinding;
3655 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003656 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
3657 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00003658 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003659 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003660#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00003661 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +00003662 sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00003663 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003664#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003665 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003666#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003667 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003668 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
3669#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003670 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003671 SkPoint lastPt;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003672 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003673 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003674 // FIXME: needs work. While it works in limited situations, it does
3675 // not always compute winding correctly. Active should be removed and instead
3676 // the initial winding should be correctly passed in so that if the
3677 // inner contour is wound the same way, it never finds an accumulated
3678 // winding of zero. Inside 'find next', we need to look for transitions
3679 // other than zero when resolving sorted angles.
caryclark@google.com27c449a2012-07-27 18:26:38 +00003680 bool active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003681 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003682 do {
caryclark@google.com0e08a192012-07-13 21:07:52 +00003683 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00003684 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
3685 __FUNCTION__, active ? "true" : "false",
3686 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003687 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003688 const SkPoint* firstPt = NULL;
3689 do {
3690 SkASSERT(!current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003691 int nextStart = index;
3692 int nextEnd = endIndex;
3693 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.com27c449a2012-07-27 18:26:38 +00003694 nextStart, nextEnd, winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003695 if (!next) {
3696 break;
3697 }
3698 if (!firstPt) {
3699 firstPt = &current->addMoveTo(index, simple, active);
3700 }
3701 lastPt = current->addCurveTo(index, endIndex, simple, active);
3702 current = next;
3703 index = nextStart;
3704 endIndex = nextEnd;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003705 } while (*firstPt != lastPt && (active || !current->done()));
3706 if (firstPt && active) {
3707 #if DEBUG_PATH_CONSTRUCTION
3708 SkDebugf("%s close\n", __FUNCTION__);
3709 #endif
3710 simple.close();
3711 }
caryclark@google.come21cb182012-07-23 21:26:31 +00003712 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003713 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00003714 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003715 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003716 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003717 break;
3718 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003719 int lesser = SkMin32(index, endIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003720 spanWinding = current->spanSign(index, endIndex);
3721 winding = current->windSum(lesser);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003722 bool inner = useInnerWinding(winding - spanWinding, winding);
3723 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00003724 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
caryclark@google.com59823f72012-08-09 18:17:47 +00003725 " inner=%d result=%d\n",
caryclark@google.com2ddff932012-08-07 21:25:27 +00003726 __FUNCTION__, current->debugID(), current->t(lesser),
3727 spanWinding, winding, SkSign32(index - endIndex),
3728 useInnerWinding(winding - spanWinding, winding),
caryclark@google.com2ddff932012-08-07 21:25:27 +00003729 inner ? winding - spanWinding : winding);
3730 #endif
3731 if (inner) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003732 winding -= spanWinding;
3733 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003734 active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003735 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003736 } while (true);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003737}
3738
caryclark@google.com24bec792012-08-20 12:43:57 +00003739static void bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
3740 Segment* current;
3741 int start, end;
3742 while ((current = findUndone(contourList, start, end))) {
3743 const SkPoint* firstPt = NULL;
3744 SkPoint lastPt;
3745 do {
3746 SkASSERT(!current->done());
3747 int nextStart = start;
3748 int nextEnd = end;
3749 Segment* next = current->findNextXor(nextStart, nextEnd);
3750 if (!next) {
3751 break;
3752 }
3753 if (!firstPt) {
3754 firstPt = &current->addMoveTo(start, simple, true);
3755 }
3756 lastPt = current->addCurveTo(start, end, simple, true);
3757 current = next;
3758 start = nextStart;
3759 end = nextEnd;
3760 } while (*firstPt != lastPt);
3761 if (firstPt) {
3762 #if DEBUG_PATH_CONSTRUCTION
3763 SkDebugf("%s close\n", __FUNCTION__);
3764 #endif
3765 simple.close();
3766 }
3767 }
3768}
3769
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003770static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
3771 int contourCount = contourList.count();
3772 for (int cTest = 0; cTest < contourCount; ++cTest) {
3773 Contour* contour = contourList[cTest];
3774 contour->fixOtherTIndex();
3775 }
3776}
3777
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003778static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003779 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003780 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003781 if (count == 0) {
3782 return;
3783 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003784 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003785 *list.append() = &contours[index];
3786 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003787 QSort<Contour>(list.begin(), list.end() - 1);
3788}
3789
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003790void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003791 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.com24bec792012-08-20 12:43:57 +00003792 int xorMask = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003793 simple.reset();
3794 simple.setFillType(SkPath::kEvenOdd_FillType);
3795
3796 // turn path into list of segments
3797 SkTArray<Contour> contours;
3798 // FIXME: add self-intersecting cubics' T values to segment
3799 EdgeBuilder builder(path, contours);
3800 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003801 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003802 Contour** currentPtr = contourList.begin();
3803 if (!currentPtr) {
3804 return;
3805 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003806 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003807 // find all intersections between segments
3808 do {
3809 Contour** nextPtr = currentPtr;
3810 Contour* current = *currentPtr++;
3811 Contour* next;
3812 do {
3813 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003814 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003815 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003816 // eat through coincident edges
caryclark@google.com24bec792012-08-20 12:43:57 +00003817 coincidenceCheck(contourList, xorMask);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003818 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003819 // construct closed contours
caryclark@google.com24bec792012-08-20 12:43:57 +00003820 if (xorMask < 0) {
3821 bridgeWinding(contourList, simple);
3822 } else {
3823 bridgeXor(contourList, simple);
3824 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003825}
3826