blob: 62439d0c0b9ab3cfcccf5183d9b3fb2d98d0ce80 [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.coma461ff02012-10-11 12:54:23 +000028#define PRECISE_T_SORT 1
caryclark@google.comfb51afb2012-10-19 15:54:16 +000029#define SORTABLE_CONTOURS 0 // set to 1 for old code that works most of the time
caryclark@google.coma461ff02012-10-11 12:54:23 +000030
caryclark@google.com47580692012-07-23 12:14:49 +000031#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfb51afb2012-10-19 15:54:16 +000032#define FORCE_RELEASE 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000033
caryclark@google.comfb51afb2012-10-19 15:54:16 +000034#if FORCE_RELEASE || defined SK_RELEASE // set force release to 1 for multiple thread -- no debugging
caryclark@google.com47580692012-07-23 12:14:49 +000035
36const bool gRunTestsInOneThread = false;
37
38#define DEBUG_ACTIVE_SPANS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000039#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000040#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000041#define DEBUG_ANGLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000042#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000043#define DEBUG_CROSS 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000044#define DEBUG_MARK_DONE 0
caryclark@google.coma461ff02012-10-11 12:54:23 +000045#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com47580692012-07-23 12:14:49 +000046#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000047#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000048#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000049
50#else
51
caryclark@google.com47580692012-07-23 12:14:49 +000052const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000053
caryclark@google.comc91dfe42012-10-16 12:06:27 +000054#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000055#define DEBUG_ADD_INTERSECTING_TS 1
56#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000057#define DEBUG_ANGLE 1
58#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000059#define DEBUG_CROSS 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000060#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000061#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000062#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000063#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000064#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000065
66#endif
67
caryclark@google.com6aea33f2012-10-09 14:11:58 +000068#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000069
caryclark@google.comfa0588f2012-04-26 21:01:06 +000070#if DEBUG_DUMP
71static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000072// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000073static int gContourID;
74static int gSegmentID;
75#endif
76
caryclark@google.com8dcf1142012-07-02 20:27:02 +000077#ifndef DEBUG_TEST
78#define DEBUG_TEST 0
79#endif
80
caryclark@google.com32546db2012-08-31 20:55:07 +000081#define MAKE_CONST_LINE(line, pts) \
82 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
83#define MAKE_CONST_QUAD(quad, pts) \
84 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
85 {pts[2].fX, pts[2].fY}}
86#define MAKE_CONST_CUBIC(cubic, pts) \
87 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
88 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
89
caryclark@google.comfa0588f2012-04-26 21:01:06 +000090static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
91 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000092 MAKE_CONST_LINE(aLine, a);
93 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +000094 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
95}
96
97static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
98 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000099 MAKE_CONST_QUAD(aQuad, a);
100 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000101 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000102}
103
caryclark@google.com32546db2012-08-31 20:55:07 +0000104static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000105 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000106 MAKE_CONST_CUBIC(aCubic, a);
107 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000108 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
109}
110
111static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
112 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000113 MAKE_CONST_QUAD(aQuad, a);
114 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000115#define TRY_QUARTIC_SOLUTION 1
116#if TRY_QUARTIC_SOLUTION
117 intersect2(aQuad, bQuad, intersections);
118#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000119 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000120#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000121 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000122}
123
124static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
125 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000126 MAKE_CONST_CUBIC(aCubic, a);
127 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000128 intersect(aCubic, bCubic, intersections);
129 return intersections.fUsed;
130}
131
132static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
133 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000134 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000135 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
136}
137
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000138static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
139 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000140 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000141 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
142}
143
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000144static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
145 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000146 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000147 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
148}
149
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000150static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
151 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000152 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000153 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
154}
155
156static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
157 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000158 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000159 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
160}
161
162static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
163 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000164 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000165 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000166}
167
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000168static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
169 SkScalar , SkScalar , bool , Intersections& ) = {
170 NULL,
171 VLineIntersect,
172 VQuadIntersect,
173 VCubicIntersect
174};
175
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000176static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000177 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000178 double x, y;
179 xy_at_t(line, t, x, y);
180 out->fX = SkDoubleToScalar(x);
181 out->fY = SkDoubleToScalar(y);
182}
183
184static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000185 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000186 double x, y;
187 xy_at_t(quad, t, x, y);
188 out->fX = SkDoubleToScalar(x);
189 out->fY = SkDoubleToScalar(y);
190}
191
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000192static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
193 MAKE_CONST_QUAD(quad, a);
194 xy_at_t(quad, t, out->x, out->y);
195}
196
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000197static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000198 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000199 double x, y;
200 xy_at_t(cubic, t, x, y);
201 out->fX = SkDoubleToScalar(x);
202 out->fY = SkDoubleToScalar(y);
203}
204
205static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
206 NULL,
207 LineXYAtT,
208 QuadXYAtT,
209 CubicXYAtT
210};
211
212static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000213 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000214 double x;
215 xy_at_t(aLine, t, x, *(double*) 0);
216 return SkDoubleToScalar(x);
217}
218
219static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000220 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000221 double x;
222 xy_at_t(quad, t, x, *(double*) 0);
223 return SkDoubleToScalar(x);
224}
225
226static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000227 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000228 double x;
229 xy_at_t(cubic, t, x, *(double*) 0);
230 return SkDoubleToScalar(x);
231}
232
233static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
234 NULL,
235 LineXAtT,
236 QuadXAtT,
237 CubicXAtT
238};
239
240static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000241 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000242 double y;
243 xy_at_t(aLine, t, *(double*) 0, y);
244 return SkDoubleToScalar(y);
245}
246
247static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000248 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000249 double y;
250 xy_at_t(quad, t, *(double*) 0, y);
251 return SkDoubleToScalar(y);
252}
253
254static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000255 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000256 double y;
257 xy_at_t(cubic, t, *(double*) 0, y);
258 return SkDoubleToScalar(y);
259}
260
261static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
262 NULL,
263 LineYAtT,
264 QuadYAtT,
265 CubicYAtT
266};
267
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000268static SkScalar LineDXAtT(const SkPoint a[2], double ) {
269 return a[1].fX - a[0].fX;
270}
271
272static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000273 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000274 double x;
275 dxdy_at_t(quad, t, x, *(double*) 0);
276 return SkDoubleToScalar(x);
277}
278
279static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000280 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000281 double x;
282 dxdy_at_t(cubic, t, x, *(double*) 0);
283 return SkDoubleToScalar(x);
284}
285
286static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
287 NULL,
288 LineDXAtT,
289 QuadDXAtT,
290 CubicDXAtT
291};
292
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000293static void LineSubDivide(const SkPoint a[2], double startT, double endT,
294 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000295 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000296 _Line dst;
297 sub_divide(aLine, startT, endT, dst);
298 sub[0].fX = SkDoubleToScalar(dst[0].x);
299 sub[0].fY = SkDoubleToScalar(dst[0].y);
300 sub[1].fX = SkDoubleToScalar(dst[1].x);
301 sub[1].fY = SkDoubleToScalar(dst[1].y);
302}
303
304static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
305 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000306 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000307 Quadratic dst;
308 sub_divide(aQuad, startT, endT, dst);
309 sub[0].fX = SkDoubleToScalar(dst[0].x);
310 sub[0].fY = SkDoubleToScalar(dst[0].y);
311 sub[1].fX = SkDoubleToScalar(dst[1].x);
312 sub[1].fY = SkDoubleToScalar(dst[1].y);
313 sub[2].fX = SkDoubleToScalar(dst[2].x);
314 sub[2].fY = SkDoubleToScalar(dst[2].y);
315}
316
317static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
318 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000319 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000320 Cubic dst;
321 sub_divide(aCubic, startT, endT, dst);
322 sub[0].fX = SkDoubleToScalar(dst[0].x);
323 sub[0].fY = SkDoubleToScalar(dst[0].y);
324 sub[1].fX = SkDoubleToScalar(dst[1].x);
325 sub[1].fY = SkDoubleToScalar(dst[1].y);
326 sub[2].fX = SkDoubleToScalar(dst[2].x);
327 sub[2].fY = SkDoubleToScalar(dst[2].y);
328 sub[3].fX = SkDoubleToScalar(dst[3].x);
329 sub[3].fY = SkDoubleToScalar(dst[3].y);
330}
331
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000332static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
333 SkPoint []) = {
334 NULL,
335 LineSubDivide,
336 QuadSubDivide,
337 CubicSubDivide
338};
339
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000340static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000341 _Line sub) {
342 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000343 _Line dst;
344 sub_divide(aLine, startT, endT, dst);
345 sub[0] = dst[0];
346 sub[1] = dst[1];
347}
348
349static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000350 Quadratic sub) {
351 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000352 Quadratic dst;
353 sub_divide(aQuad, startT, endT, dst);
354 sub[0] = dst[0];
355 sub[1] = dst[1];
356 sub[2] = dst[2];
357}
358
359static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000360 Cubic sub) {
361 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000362 Cubic dst;
363 sub_divide(aCubic, startT, endT, dst);
364 sub[0] = dst[0];
365 sub[1] = dst[1];
366 sub[2] = dst[2];
367 sub[3] = dst[3];
368}
369
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000370#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000371static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
372 SkRect& bounds) {
373 SkPoint dst[3];
374 QuadSubDivide(a, startT, endT, dst);
375 bounds.fLeft = bounds.fRight = dst[0].fX;
376 bounds.fTop = bounds.fBottom = dst[0].fY;
377 for (int index = 1; index < 3; ++index) {
378 bounds.growToInclude(dst[index].fX, dst[index].fY);
379 }
380}
381
382static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
383 SkRect& bounds) {
384 SkPoint dst[4];
385 CubicSubDivide(a, startT, endT, dst);
386 bounds.fLeft = bounds.fRight = dst[0].fX;
387 bounds.fTop = bounds.fBottom = dst[0].fY;
388 for (int index = 1; index < 4; ++index) {
389 bounds.growToInclude(dst[index].fX, dst[index].fY);
390 }
391}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000392#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000393
caryclark@google.com15fa1382012-05-07 20:49:36 +0000394static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000395 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000396 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000397 Quadratic dst;
398 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000399 if (order == 2) { // quad became line
400 for (int index = 0; index < order; ++index) {
401 SkPoint* pt = reducePts.append();
402 pt->fX = SkDoubleToScalar(dst[index].x);
403 pt->fY = SkDoubleToScalar(dst[index].y);
404 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000405 }
406 return (SkPath::Verb) (order - 1);
407}
408
409static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
410 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000411 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000412 Cubic dst;
413 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000414 if (order == 2 || order == 3) { // cubic became line or quad
415 for (int index = 0; index < order; ++index) {
416 SkPoint* pt = reducePts.append();
417 pt->fX = SkDoubleToScalar(dst[index].x);
418 pt->fY = SkDoubleToScalar(dst[index].y);
419 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000420 }
421 return (SkPath::Verb) (order - 1);
422}
423
caryclark@google.com15fa1382012-05-07 20:49:36 +0000424static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000425 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000426 return isLinear(aQuad, 0, 2);
427}
428
429static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000430 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000431 return isLinear(aCubic, 0, 3);
432}
433
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000434static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000435 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000436 double x[2];
437 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000438 xy_at_t(aLine, endT, x[1], *(double*) 0);
439 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000440}
441
442static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000443 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000444 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000445}
446
447static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000448 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000449 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000450}
451
452static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
453 NULL,
454 LineLeftMost,
455 QuadLeftMost,
456 CubicLeftMost
457};
458
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000459#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000460static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
461 Intersections& intersections) {
462 MAKE_CONST_QUAD(aQuad, a);
463 MAKE_CONST_LINE(bLine, b);
464 return intersectRay(aQuad, bLine, intersections);
465}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000466#endif
467
468static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
469 Intersections& intersections) {
470 MAKE_CONST_QUAD(aQuad, a);
471 return intersectRay(aQuad, bLine, intersections);
472}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000473
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000474class Segment;
475
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000476struct Span {
477 Segment* fOther;
478 mutable SkPoint fPt; // lazily computed as needed
479 double fT;
480 double fOtherT; // value at fOther[fOtherIndex].fT
481 int fOtherIndex; // can't be used during intersection
482 int fWindSum; // accumulated from contours surrounding this one
483 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
484 int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
485 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000486 bool fUnsortableStart; // set when start is part of an unsortable pair
487 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000488};
489
caryclark@google.com15fa1382012-05-07 20:49:36 +0000490// sorting angles
491// given angles of {dx dy ddx ddy dddx dddy} sort them
492class Angle {
493public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000494 // FIXME: this is bogus for quads and cubics
495 // if the quads and cubics' line from end pt to ctrl pt are coincident,
496 // there's no obvious way to determine the curve ordering from the
497 // derivatives alone. In particular, if one quadratic's coincident tangent
498 // is longer than the other curve, the final control point can place the
499 // longer curve on either side of the shorter one.
500 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
501 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000502
caryclark@google.com32546db2012-08-31 20:55:07 +0000503 /*(
504 for quads and cubics, set up a parameterized line (e.g. LineParameters )
505 for points [0] to [1]. See if point [2] is on that line, or on one side
506 or the other. If it both quads' end points are on the same side, choose
507 the shorter tangent. If the tangents are equal, choose the better second
508 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000509
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000510 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000511 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000512 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000513 double y = dy();
514 double ry = rh.dy();
515 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
516 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000517 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000518 double x = dx();
519 double rx = rh.dx();
520 if (y == 0 && ry == 0 && x * rx < 0) {
521 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000522 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000523 double x_ry = x * ry;
524 double rx_y = rx * y;
525 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000526 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000527 return cmp < 0;
528 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000529 if (approximately_zero(x_ry) && approximately_zero(rx_y)
530 && !approximately_zero_squared(cmp)) {
531 return cmp < 0;
532 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000533 // at this point, the initial tangent line is coincident
534 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) || !approximately_zero(rh.fSide))) {
535 // FIXME: running demo will trigger this assertion
536 // (don't know if commenting out will trigger further assertion or not)
537 // commenting it out allows demo to run in release, though
538 // SkASSERT(fSide != rh.fSide);
539 return fSide < rh.fSide;
540 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000541 // see if either curve can be lengthened and try the tangent compare again
542 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
543 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
544 Angle longer = *this;
545 Angle rhLonger = rh;
546 if (longer.lengthen() | rhLonger.lengthen()) {
547 return longer < rhLonger;
548 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000549 // what if we extend in the other direction?
550 longer = *this;
551 rhLonger = rh;
552 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
553 return longer < rhLonger;
554 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000555 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000556 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
557 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000558 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000559 // end of the shorter tangent to midway between the end points
560 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000561 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
562 double len = fTangent1.normalSquared();
563 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000564 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000565 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000566 int roots, rroots;
567 bool flip = false;
568 do {
569 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
570 double midX = (q[0].x + q[2].x) / 2;
571 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000572 ray[0] = q[1];
573 ray[1].x = midX;
574 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000575 SkASSERT(ray[0] != ray[1]);
576 roots = QuadRayIntersect(fPts, ray, i);
577 rroots = QuadRayIntersect(rh.fPts, ray, ri);
578 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000579 if (roots == 0 || rroots == 0) {
580 // FIXME: we don't have a solution in this case. The interim solution
581 // is to mark the edges as unsortable, exclude them from this and
582 // future computations, and allow the returned path to be fragmented
583 fUnsortable = true;
584 rh.fUnsortable = true;
585 return this < &rh; // even with no solution, return a stable sort
586 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000587 _Point loc;
588 double best = SK_ScalarInfinity;
589 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000590 int index;
591 for (index = 0; index < roots; ++index) {
592 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000593 dx = loc.x - ray[0].x;
594 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000595 dist = dx * dx + dy * dy;
596 if (best > dist) {
597 best = dist;
598 }
599 }
600 for (index = 0; index < rroots; ++index) {
601 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000602 dx = loc.x - ray[0].x;
603 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000604 dist = dx * dx + dy * dy;
605 if (best > dist) {
606 return fSide < 0;
607 }
608 }
609 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000610 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000611
caryclark@google.com47580692012-07-23 12:14:49 +0000612 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000613 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000614 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000615
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000616 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000617 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000618 }
619
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000620 int end() const {
621 return fEnd;
622 }
623
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000624 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000625 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000626 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000627
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000628 bool lengthen() {
629 int newEnd = fEnd;
630 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
631 fEnd = newEnd;
632 setSpans();
633 return true;
634 }
635 return false;
636 }
637
caryclark@google.coma461ff02012-10-11 12:54:23 +0000638 bool reverseLengthen() {
639 if (fReversed) {
640 return false;
641 }
642 int newEnd = fStart;
643 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
644 fEnd = newEnd;
645 fReversed = true;
646 setSpans();
647 return true;
648 }
649 return false;
650 }
651
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000652 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000653 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000654 fSegment = segment;
655 fStart = start;
656 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000657 fPts = orig;
658 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000659 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000660 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000661 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000662 setSpans();
663 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000664
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000665 void setSpans() {
666 double startT = (*fSpans)[fStart].fT;
667 double endT = (*fSpans)[fEnd].fT;
668 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000669 case SkPath::kLine_Verb:
670 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000671 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000672 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
673 fTangent1.lineEndPoints(l);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000674 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000675 break;
676 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000677 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000678 fTangent1.quadEndPoints(fQ, 0, 1);
679 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000680 break;
681 case SkPath::kCubic_Verb:
682 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000683 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000684 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000685 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000686 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000687 default:
688 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000689 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000690 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000691
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000692 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000693 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000694 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000695
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000696 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000697 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000698 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000699
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000700 const SkTDArray<Span>* spans() const {
701 return fSpans;
702 }
703
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000704 int start() const {
705 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000706 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000707
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000708 bool unsortable() const {
709 return fUnsortable;
710 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000711
caryclark@google.comc899ad92012-08-23 15:24:42 +0000712#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000713 const SkPoint* pts() const {
714 return fPts;
715 }
716
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000717 SkPath::Verb verb() const {
718 return fVerb;
719 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000720
caryclark@google.comc899ad92012-08-23 15:24:42 +0000721 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000722 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000723 }
724#endif
725
caryclark@google.com15fa1382012-05-07 20:49:36 +0000726private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000727 const SkPoint* fPts;
728 Quadratic fQ;
729 SkPath::Verb fVerb;
730 double fSide;
731 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000732 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000733 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000734 int fStart;
735 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000736 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000737 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000738};
739
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000740// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000741struct Bounds : public SkRect {
742 static bool Intersects(const Bounds& a, const Bounds& b) {
743 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
744 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
745 }
746
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000747 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
748 if (left < fLeft) {
749 fLeft = left;
750 }
751 if (top < fTop) {
752 fTop = top;
753 }
754 if (right > fRight) {
755 fRight = right;
756 }
757 if (bottom > fBottom) {
758 fBottom = bottom;
759 }
760 }
761
762 void add(const Bounds& toAdd) {
763 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
764 }
765
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000766 bool isEmpty() {
767 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000768 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000769 || isnan(fLeft) || isnan(fRight)
770 || isnan(fTop) || isnan(fBottom);
771 }
772
773 void setCubicBounds(const SkPoint a[4]) {
774 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000775 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000776 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000777 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
778 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000779 }
780
781 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000782 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000783 _Rect dRect;
784 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000785 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
786 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000787 }
788};
789
caryclark@google.com2ddff932012-08-07 21:25:27 +0000790static bool useInnerWinding(int outerWinding, int innerWinding) {
791 SkASSERT(outerWinding != innerWinding);
792 int absOut = abs(outerWinding);
793 int absIn = abs(innerWinding);
794 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
795 if (outerWinding * innerWinding < 0) {
796#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000797 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000798 outerWinding, innerWinding, result ? "true" : "false");
799#endif
800 }
801 return result;
802}
803
caryclark@google.com235f56a2012-09-14 14:19:30 +0000804static const bool opLookup[][2][2] = {
805 // ==0 !=0
806 // b a b a
807 {{true , false}, {false, true }}, // a - b
808 {{false, false}, {true , true }}, // a & b
809 {{true , true }, {false, false}}, // a | b
810 {{true , true }, {true , true }}, // a ^ b
811};
812
813static bool activeOp(bool angleIsOp, int otherNonZero, ShapeOp op) {
814 return opLookup[op][otherNonZero][angleIsOp];
815}
816
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000817class Segment {
818public:
819 Segment() {
820#if DEBUG_DUMP
821 fID = ++gSegmentID;
822#endif
823 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000824
825 bool operator<(const Segment& rh) const {
826 return fBounds.fTop < rh.fBounds.fTop;
827 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000828
caryclark@google.com9764cc62012-07-12 19:29:45 +0000829 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
830 if (activeAngleInner(index, done, angles)) {
831 return true;
832 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000833 double referenceT = fTs[index].fT;
834 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000835 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000836 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000837 return true;
838 }
839 }
840 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000841 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000842 return true;
843 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000844 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000845 return false;
846 }
847
caryclark@google.com9764cc62012-07-12 19:29:45 +0000848 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000849 Span* span = &fTs[index];
850 Segment* other = span->fOther;
851 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +0000852 return other->activeAngleInner(oIndex, done, angles);
853 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000854
caryclark@google.com9764cc62012-07-12 19:29:45 +0000855 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000856 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +0000857 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000858 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000859 if (upSpan.fWindValue) {
860 addAngle(angles, index, next);
861 if (upSpan.fDone) {
862 done++;
863 } else if (upSpan.fWindSum != SK_MinS32) {
864 return true;
865 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000866 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000867 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000868 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000869 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +0000870 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000871 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000872 if (downSpan.fWindValue) {
873 addAngle(angles, index, prev);
874 if (downSpan.fDone) {
875 done++;
876 } else if (downSpan.fWindSum != SK_MinS32) {
877 return true;
878 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000879 }
880 }
881 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000882 }
883
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000884 SkScalar activeTop() const {
885 SkASSERT(!done());
886 int count = fTs.count();
887 SkScalar result = SK_ScalarMax;
888 bool lastDone = true;
889 for (int index = 0; index < count; ++index) {
890 bool done = fTs[index].fDone;
891 if (!done || !lastDone) {
892 SkScalar y = yAtT(index);
893 if (result > y) {
894 result = y;
895 }
896 }
897 lastDone = done;
898 }
899 SkASSERT(result < SK_ScalarMax);
900 return result;
901 }
902
903 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000904 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000905 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000906#if DEBUG_ANGLE
907 if (angles.count() > 1) {
908 SkPoint angle0Pt, newPt;
909 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
910 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
911 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
912 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
913 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
914 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000915#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000916 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000917 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000918
caryclark@google.com2ddff932012-08-07 21:25:27 +0000919 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +0000920 double oEnd) {
921 int tIndex = -1;
922 int tCount = fTs.count();
923 int oIndex = -1;
924 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +0000925 do {
926 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000927 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +0000928 int tIndexStart = tIndex;
929 do {
930 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000931 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +0000932 int oIndexStart = oIndex;
933 double nextT;
934 do {
935 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000936 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000937 double oNextT;
938 do {
939 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000940 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000941 // at this point, spans before and after are at:
942 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
943 // if tIndexStart == 0, no prior span
944 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +0000945
caryclark@google.comcc905052012-07-25 20:59:42 +0000946 // advance the span with zero winding
947 // if the following span exists (not past the end, non-zero winding)
948 // connect the two edges
949 if (!fTs[tIndexStart].fWindValue) {
950 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
951 #if DEBUG_CONCIDENT
952 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
953 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000954 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
955 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +0000956 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000957 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000958 }
959 if (nextT < 1 && fTs[tIndex].fWindValue) {
960 #if DEBUG_CONCIDENT
961 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
962 __FUNCTION__, fID, other.fID, tIndex,
963 fTs[tIndex].fT, xyAtT(tIndex).fX,
964 xyAtT(tIndex).fY);
965 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000966 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000967 }
968 } else {
969 SkASSERT(!other.fTs[oIndexStart].fWindValue);
970 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
971 #if DEBUG_CONCIDENT
972 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
973 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000974 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
975 other.xyAtT(oIndexStart).fY);
976 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +0000977 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +0000978 }
979 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
980 #if DEBUG_CONCIDENT
981 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
982 __FUNCTION__, fID, other.fID, oIndex,
983 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
984 other.xyAtT(oIndex).fY);
985 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
986 #endif
987 }
988 }
989 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000990
caryclark@google.comcc905052012-07-25 20:59:42 +0000991 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
992 double oEnd) {
993 // walk this to outsideTs[0]
994 // walk other to outsideTs[1]
995 // if either is > 0, add a pointer to the other, copying adjacent winding
996 int tIndex = -1;
997 int oIndex = -1;
998 double tStart = outsideTs[0];
999 double oStart = outsideTs[1];
1000 do {
1001 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001002 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001003 do {
1004 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001005 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001006 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001007 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001008 }
1009 tStart = fTs[tIndex].fT;
1010 oStart = other.fTs[oIndex].fT;
1011 do {
1012 double nextT;
1013 do {
1014 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001015 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001016 tStart = nextT;
1017 do {
1018 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001019 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001020 oStart = nextT;
1021 if (tStart == 1 && oStart == 1) {
1022 break;
1023 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001024 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001025 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001026 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001027
caryclark@google.com235f56a2012-09-14 14:19:30 +00001028 void addCubic(const SkPoint pts[4], bool operand) {
1029 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001030 fBounds.setCubicBounds(pts);
1031 }
1032
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001033 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001034 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001035 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001036 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001037 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001038 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001039 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001040 SkDebugf("path.%sTo(%1.9g,%1.9g",
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001041 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
1042 if (fVerb > 1) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001043 SkDebugf(", %1.9g,%1.9g", edge[2].fX, edge[2].fY);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001044 }
1045 if (fVerb > 2) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001046 SkDebugf(", %1.9g,%1.9g", edge[3].fX, edge[3].fY);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001047 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001048 SkDebugf(");\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001049 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001050 switch (fVerb) {
1051 case SkPath::kLine_Verb:
1052 path.lineTo(edge[1].fX, edge[1].fY);
1053 break;
1054 case SkPath::kQuad_Verb:
1055 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
1056 break;
1057 case SkPath::kCubic_Verb:
1058 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
1059 edge[3].fX, edge[3].fY);
1060 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001061 default:
1062 SkASSERT(0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001063 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001064 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001065 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001066 }
1067
caryclark@google.com235f56a2012-09-14 14:19:30 +00001068 void addLine(const SkPoint pts[2], bool operand) {
1069 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001070 fBounds.set(pts, 2);
1071 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001072
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001073 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
1074 const SkPoint& pt = xyAtT(tIndex);
1075 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001076 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001077 SkDebugf("path.moveTo(%1.9g, %1.9g);\n", pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001078 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001079 path.moveTo(pt.fX, pt.fY);
1080 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001081 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001082 }
1083
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001084 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001085 void addOtherT(int index, double otherT, int otherIndex) {
1086 Span& span = fTs[index];
1087 span.fOtherT = otherT;
1088 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001089 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001090
caryclark@google.com235f56a2012-09-14 14:19:30 +00001091 void addQuad(const SkPoint pts[3], bool operand) {
1092 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001093 fBounds.setQuadBounds(pts);
1094 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001095
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001096 // Defer all coincident edge processing until
1097 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001098
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001099// no need to be tricky; insert in normal T order
1100// resolve overlapping ts when considering coincidence later
1101
1102 // add non-coincident intersection. Resulting edges are sorted in T.
1103 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001104 // FIXME: in the pathological case where there is a ton of intercepts,
1105 // binary search?
1106 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001107 size_t tCount = fTs.count();
caryclark@google.comc899ad92012-08-23 15:24:42 +00001108 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001109 if (approximately_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001110 newT = 0;
1111 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001112 if (approximately_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001113 newT = 1;
1114 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001115 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001116 // OPTIMIZATION: if there are three or more identical Ts, then
1117 // the fourth and following could be further insertion-sorted so
1118 // that all the edges are clockwise or counterclockwise.
1119 // This could later limit segment tests to the two adjacent
1120 // neighbors, although it doesn't help with determining which
1121 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001122 if (newT < fTs[index].fT) {
1123 insertedAt = index;
1124 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001125 }
1126 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001127 Span* span;
1128 if (insertedAt >= 0) {
1129 span = fTs.insert(insertedAt);
1130 } else {
1131 insertedAt = tCount;
1132 span = fTs.append();
1133 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001134 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001135 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001136 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001137 span->fWindSum = SK_MinS32;
1138 span->fWindValue = 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001139 span->fWindValueOpp = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001140 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001141 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001142 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001143 span->fUnsortableStart = false;
1144 span->fUnsortableEnd = false;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001145 return insertedAt;
1146 }
1147
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001148 // set spans from start to end to decrement by one
1149 // note this walks other backwards
1150 // FIMXE: there's probably an edge case that can be constructed where
1151 // two span in one segment are separated by float epsilon on one span but
1152 // not the other, if one segment is very small. For this
1153 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001154 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001155 // sorted? It feels better in such a case to match the span's other span
1156 // pointer since both coincident segments must contain the same spans.
1157 void addTCancel(double startT, double endT, Segment& other,
1158 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001159 SkASSERT(!approximately_negative(endT - startT));
1160 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001161 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001162 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001163 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001164 ++index;
1165 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001166 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001167 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001168 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001169 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001170 Span* test = &fTs[index];
1171 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001172 SkTDArray<double> outsideTs;
1173 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001174 do {
1175 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001176 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001177 double testT = test->fT;
1178 double oTestT = oTest->fT;
1179 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001180 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001181 if (decrement) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001182 if (binary) {
1183 --(span->fWindValueOpp);
1184 } else {
1185 decrementSpan(span);
1186 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001187 } else if (track && span->fT < 1 && oTestT < 1) {
1188 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001189 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001190 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001191 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001192 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001193 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1194 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1195 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001196 while (approximately_negative(otherTMatchStart - oSpan->fT)
1197 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001198 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001199 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001200 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001201 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001202 other.decrementSpan(oSpan);
1203 } else if (track && oSpan->fT < 1 && testT < 1) {
1204 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001205 }
1206 if (!oIndex) {
1207 break;
1208 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001209 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001210 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001211 test = span;
1212 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001213 } while (!approximately_negative(endT - test->fT));
1214 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001215 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001216 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001217 double tStart = outsideTs[0];
1218 double oStart = outsideTs[1];
1219 addCancelOutsides(tStart, oStart, other, oEndT);
1220 int count = outsideTs.count();
1221 if (count > 2) {
1222 double tStart = outsideTs[count - 2];
1223 double oStart = outsideTs[count - 1];
1224 addCancelOutsides(tStart, oStart, other, oEndT);
1225 }
caryclark@google.com18063442012-07-25 12:05:18 +00001226 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001227 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001228 double tStart = oOutsideTs[0];
1229 double oStart = oOutsideTs[1];
1230 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001231 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001232 }
1233
1234 // set spans from start to end to increment the greater by one and decrement
1235 // the lesser
caryclark@google.com235f56a2012-09-14 14:19:30 +00001236 void addTCoincident(const bool isXor, double startT, double endT,
1237 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001238 SkASSERT(!approximately_negative(endT - startT));
1239 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001240 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001241 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001242 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001243 ++index;
1244 }
1245 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001246 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001247 ++oIndex;
1248 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001249 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001250 Span* test = &fTs[index];
1251 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001252 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001253 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001254 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001255 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001256 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001257 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001258 bool decrementThis = (test->fWindValue < oTest->fWindValue) & !isXor;
1259 bool decrementOther = (test->fWindValue >= oTest->fWindValue) & !isXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001260 Span* end = test;
1261 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001262 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001263 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001264 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001265 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001266 if (transfer) {
1267 if (decrementOther) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001268 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001269 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001270 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001271 if (binary) {
1272 ++(end->fWindValueOpp);
1273 } else {
1274 ++(end->fWindValue);
1275 }
caryclark@google.com18063442012-07-25 12:05:18 +00001276 } else if (decrementSpan(end)) {
1277 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001278 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001279 } else if (oTest->fWindValue) {
1280 SkASSERT(!decrementOther);
1281 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1282 TrackOutside(xOutsideTs, end->fT, oStartT);
1283 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001284 }
1285 end = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001286 } while (approximately_negative(end->fT - test->fT));
caryclark@google.com59823f72012-08-09 18:17:47 +00001287 // because of the order in which coincidences are resolved, this and other
1288 // may not have the same intermediate points. Compute the corresponding
1289 // intermediate T values (using this as the master, other as the follower)
1290 // and walk other conditionally -- hoping that it catches up in the end
1291 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001292 Span* oEnd = oTest;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001293 while (!approximately_negative(oEndT - oEnd->fT) && approximately_negative(oEnd->fT - otherTMatch)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00001294 if (transfer) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001295 if (decrementThis) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001296 #ifdef SK_DEBUG
1297 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1298 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001299 if (binary) {
1300 ++(oEnd->fWindValueOpp);
1301 } else {
1302 ++(oEnd->fWindValue);
1303 }
caryclark@google.com18063442012-07-25 12:05:18 +00001304 } else if (other.decrementSpan(oEnd)) {
1305 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001306 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001307 } else if (test->fWindValue) {
1308 SkASSERT(!decrementOther);
1309 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1310 SkASSERT(0); // track for later?
1311 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001312 }
1313 oEnd = &other.fTs[++oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001314 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001315 test = end;
1316 oTest = oEnd;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001317 } while (!approximately_negative(endT - test->fT));
1318 SkASSERT(approximately_negative(oTest->fT - oEndT));
1319 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001320 if (!done()) {
1321 if (outsideTs.count()) {
1322 addCoinOutsides(outsideTs, other, oEndT);
1323 }
1324 if (xOutsideTs.count()) {
1325 addCoinOutsides(xOutsideTs, other, oEndT);
1326 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001327 }
1328 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001329 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001330 }
1331 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001332
caryclark@google.comcc905052012-07-25 20:59:42 +00001333 // FIXME: this doesn't prevent the same span from being added twice
1334 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001335 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001336 int tCount = fTs.count();
1337 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1338 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001339 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001340 break;
1341 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001342 if (approximately_negative(span.fT - t) && span.fOther == &other
1343 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001344#if DEBUG_ADD_T_PAIR
1345 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1346 __FUNCTION__, fID, t, other.fID, otherT);
1347#endif
1348 return;
1349 }
1350 }
caryclark@google.com47580692012-07-23 12:14:49 +00001351#if DEBUG_ADD_T_PAIR
1352 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1353 __FUNCTION__, fID, t, other.fID, otherT);
1354#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001355 int insertedAt = addT(t, &other);
1356 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001357 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001358 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001359 matchWindingValue(insertedAt, t, borrowWind);
1360 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001361 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001362
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001363 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001364 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001365 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1366 addAngle(angles, end, start);
1367 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001368 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001369 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001370 int tIndex = nextExactSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001371 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001372 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001373 }
1374 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001375
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001376 const Bounds& bounds() const {
1377 return fBounds;
1378 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001379
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001380 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001381 double referenceT = fTs[index].fT;
1382 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001383 #if PRECISE_T_SORT
1384 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
1385 buildAnglesInner(lesser, angles);
1386 }
1387 do {
1388 buildAnglesInner(index, angles);
1389 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
1390 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001391 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001392 buildAnglesInner(lesser, angles);
1393 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001394 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001395 buildAnglesInner(index, angles);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001396 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00001397 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001398 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001399
1400 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1401 Span* span = &fTs[index];
1402 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001403 // if there is only one live crossing, and no coincidence, continue
1404 // in the same direction
1405 // if there is coincidence, the only choice may be to reverse direction
1406 // find edge on either side of intersection
1407 int oIndex = span->fOtherIndex;
1408 // if done == -1, prior span has already been processed
1409 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001410 #if PRECISE_T_SORT
1411 int next = other->nextExactSpan(oIndex, step);
1412 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001413 int next = other->nextSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001414 #endif
1415 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001416 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001417 #if PRECISE_T_SORT
1418 next = other->nextExactSpan(oIndex, step);
1419 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001420 next = other->nextSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001421 #endif
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001422 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001423 // add candidate into and away from junction
1424 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001425 }
1426
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001427 // figure out if the segment's ascending T goes clockwise or not
1428 // not enough context to write this as shown
1429 // instead, add all segments meeting at the top
1430 // sort them using buildAngleList
1431 // find the first in the sort
1432 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001433 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001434 SkASSERT(0); // incomplete
1435 return false;
1436 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001437
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001438 // FIXME may not need this at all
1439 // FIXME once I figure out the logic, merge this and too close to call
1440 // NOTE not sure if tiny triangles can ever form at the edge, so until we
1441 // see one, only worry about triangles that happen away from 0 and 1
1442 void collapseTriangles(bool isXor) {
1443 if (fTs.count() < 3) { // require t=0, x, 1 at minimum
1444 return;
1445 }
1446 int lastIndex = 1;
1447 double lastT;
1448 while (approximately_less_than_zero((lastT = fTs[lastIndex].fT))) {
1449 ++lastIndex;
1450 }
1451 if (approximately_greater_than_one(lastT)) {
1452 return;
1453 }
1454 int matchIndex = lastIndex;
1455 do {
1456 Span& match = fTs[++matchIndex];
1457 double matchT = match.fT;
1458 if (approximately_greater_than_one(matchT)) {
1459 return;
1460 }
1461 if (matchT == lastT) {
1462 goto nextSpan;
1463 }
1464 if (approximately_negative(matchT - lastT)) {
1465 Span& last = fTs[lastIndex];
1466 Segment* lOther = last.fOther;
1467 double lT = last.fOtherT;
1468 if (approximately_less_than_zero(lT) || approximately_greater_than_one(lT)) {
1469 goto nextSpan;
1470 }
1471 Segment* mOther = match.fOther;
1472 double mT = match.fOtherT;
1473 if (approximately_less_than_zero(mT) || approximately_greater_than_one(mT)) {
1474 goto nextSpan;
1475 }
1476 // add new point to connect adjacent spurs
1477 int count = lOther->fTs.count();
1478 for (int index = 0; index < count; ++index) {
1479 Span& span = lOther->fTs[index];
1480 if (span.fOther == mOther && approximately_zero(span.fOtherT - mT)) {
1481 goto nextSpan;
1482 }
1483 }
1484 mOther->addTPair(mT, *lOther, lT, false);
1485 // FIXME ? this could go on to detect that spans on mOther, lOther are now coincident
1486 }
1487 nextSpan:
1488 lastIndex = matchIndex;
1489 lastT = matchT;
1490 } while (true);
1491 }
1492
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001493 int computeSum(int startIndex, int endIndex) {
1494 SkTDArray<Angle> angles;
1495 addTwoAngles(startIndex, endIndex, angles);
1496 buildAngles(endIndex, angles);
caryclark@google.comd1688742012-09-18 20:08:37 +00001497 // OPTIMIZATION: check all angles to see if any have computed wind sum
1498 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001499 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001500 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001501#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00001502 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001503#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001504 if (!sortable) {
1505 return SK_MinS32;
1506 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001507 int angleCount = angles.count();
1508 const Angle* angle;
1509 const Segment* base;
1510 int winding;
1511 int firstIndex = 0;
1512 do {
1513 angle = sorted[firstIndex];
1514 base = angle->segment();
1515 winding = base->windSum(angle);
1516 if (winding != SK_MinS32) {
1517 break;
1518 }
1519 if (++firstIndex == angleCount) {
1520 return SK_MinS32;
1521 }
1522 } while (true);
1523 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001524 int spanWinding = base->spanSign(angle);
1525 bool inner = useInnerWinding(winding + spanWinding, winding);
1526 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001527 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001528 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001529 inner ? winding + spanWinding : winding);
1530 #endif
1531 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001532 winding += spanWinding;
1533 }
1534 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001535 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001536 #endif
1537 int nextIndex = firstIndex + 1;
1538 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001539 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001540 do {
1541 if (nextIndex == angleCount) {
1542 nextIndex = 0;
1543 }
1544 angle = sorted[nextIndex];
1545 Segment* segment = angle->segment();
1546 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001547 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001548 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001549 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001550 maxWinding = winding;
1551 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001552 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001553 }
1554 } while (++nextIndex != lastIndex);
1555 return windSum(SkMin32(startIndex, endIndex));
1556 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001557
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001558 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001559 int bestT = -1;
1560 SkScalar top = bounds().fTop;
1561 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001562 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001563 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001564 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001565 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001566 if (fTs[start].fWindValue == 0) {
1567 continue;
1568 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001569 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001570 double startT = fTs[start].fT;
1571 double endT = fTs[end].fT;
1572 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001573 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001574 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001575 // FIXME: always use original and limit results to T values within
1576 // start t and end t.
1577 // OPTIMIZE: use specialty function that intersects ray with curve,
1578 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001579 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1580 false, intersections);
1581 if (pts == 0) {
1582 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001583 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001584 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1585 // if the intersection is edge on, wait for another one
1586 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001587 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001588 for (int index = 0; index < pts; ++index) {
1589 SkPoint pt;
1590 double foundT = intersections.fT[0][index];
1591 double testT = startT + (endT - startT) * foundT;
1592 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1593 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001594 if (fVerb > SkPath::kLine_Verb
1595 && !approximately_less_than_zero(foundT)
1596 && !approximately_greater_than_one(foundT)) {
1597 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1598 if (approximately_zero(dx)) {
1599 continue;
1600 }
1601 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001602 bestY = pt.fY;
1603 bestT = foundT < 1 ? start : end;
1604 hitT = testT;
1605 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001606 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001607 } while (fTs[end].fT != 1);
1608 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001609 }
caryclark@google.com18063442012-07-25 12:05:18 +00001610
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001611 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1612 // if a segment is connected to this one, consider it crossing
1613 int tIndex;
1614 if (fPts[0].fX == basePt.fX) {
1615 tIndex = 0;
1616 do {
1617 const Span& sSpan = fTs[tIndex];
1618 const Segment* sOther = sSpan.fOther;
1619 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1620 continue;
1621 }
1622 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1623 : sOther->fBounds.fRight > basePt.fX) {
1624 return true;
1625 }
1626 } while (fTs[++tIndex].fT == 0);
1627 }
1628 if (fPts[fVerb].fX == basePt.fX) {
1629 tIndex = fTs.count() - 1;
1630 do {
1631 const Span& eSpan = fTs[tIndex];
1632 const Segment* eOther = eSpan.fOther;
1633 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1634 continue;
1635 }
1636 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1637 : eOther->fBounds.fRight > basePt.fX) {
1638 return true;
1639 }
1640 } while (fTs[--tIndex].fT == 1);
1641 }
1642 return false;
1643 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001644
caryclark@google.com18063442012-07-25 12:05:18 +00001645 bool decrementSpan(Span* span) {
1646 SkASSERT(span->fWindValue > 0);
1647 if (--(span->fWindValue) == 0) {
1648 span->fDone = true;
1649 ++fDoneSpans;
1650 return true;
1651 }
1652 return false;
1653 }
1654
caryclark@google.com15fa1382012-05-07 20:49:36 +00001655 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001656 SkASSERT(fDoneSpans <= fTs.count());
1657 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001658 }
1659
caryclark@google.com47580692012-07-23 12:14:49 +00001660 bool done(const Angle& angle) const {
1661 int start = angle.start();
1662 int end = angle.end();
1663 const Span& mSpan = fTs[SkMin32(start, end)];
1664 return mSpan.fDone;
1665 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001666
caryclark@google.com235f56a2012-09-14 14:19:30 +00001667 Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00001668 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001669 bool& unsortable, ShapeOp op,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001670 const int aXorMask, const int bXorMask) {
1671 const int startIndex = nextStart;
1672 const int endIndex = nextEnd;
1673 int outerWinding = winding;
1674 int innerWinding = winding + spanWinding;
1675 #if DEBUG_WINDING
1676 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1677 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
1678 #endif
1679 if (useInnerWinding(outerWinding, innerWinding)) {
1680 outerWinding = innerWinding;
1681 }
1682 SkASSERT(startIndex != endIndex);
1683 int count = fTs.count();
1684 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1685 : startIndex > 0);
1686 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001687 #if PRECISE_T_SORT
1688 int end = nextExactSpan(startIndex, step);
1689 #else
caryclark@google.com235f56a2012-09-14 14:19:30 +00001690 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001691 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001692 SkASSERT(end >= 0);
1693 Span* endSpan = &fTs[end];
1694 Segment* other;
1695 if (isSimple(end)) {
1696 // mark the smaller of startIndex, endIndex done, and all adjacent
1697 // spans with the same T value (but not 'other' spans)
1698 #if DEBUG_WINDING
1699 SkDebugf("%s simple\n", __FUNCTION__);
1700 #endif
1701 markDone(SkMin32(startIndex, endIndex), outerWinding);
1702 other = endSpan->fOther;
1703 nextStart = endSpan->fOtherIndex;
1704 double startT = other->fTs[nextStart].fT;
1705 nextEnd = nextStart;
1706 do {
1707 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001708 }
1709 #if PRECISE_T_SORT
1710 while (precisely_zero(startT - other->fTs[nextEnd].fT));
1711 #else
1712 while (approximately_zero(startT - other->fTs[nextEnd].fT));
1713 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001714 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1715 return other;
1716 }
1717 // more than one viable candidate -- measure angles to find best
1718 SkTDArray<Angle> angles;
1719 SkASSERT(startIndex - endIndex != 0);
1720 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1721 addTwoAngles(startIndex, end, angles);
1722 buildAngles(end, angles);
1723 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001724 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001725 int angleCount = angles.count();
1726 int firstIndex = findStartingEdge(sorted, startIndex, end);
1727 SkASSERT(firstIndex >= 0);
1728 #if DEBUG_SORT
1729 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
1730 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001731 if (!sortable) {
1732 unsortable = true;
1733 return NULL;
1734 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001735 SkASSERT(sorted[firstIndex]->segment() == this);
1736 #if DEBUG_WINDING
1737 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
1738 #endif
1739 int aSumWinding = winding;
1740 int bSumWinding = winding;
1741 bool angleIsOp = sorted[firstIndex]->segment()->operand();
1742 int angleSpan = spanSign(sorted[firstIndex]);
1743 if (angleIsOp) {
1744 bSumWinding -= angleSpan;
1745 } else {
1746 aSumWinding -= angleSpan;
1747 }
1748 int nextIndex = firstIndex + 1;
1749 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1750 const Angle* foundAngle = NULL;
1751 // FIXME: found done logic probably fails if there are more than 4
1752 // sorted angles. It should bias towards the first and last undone
1753 // edges -- but not sure that it won't choose a middle (incorrect)
1754 // edge if one is undone
1755 bool foundDone = false;
1756 bool foundDone2 = false;
1757 // iterate through the angle, and compute everyone's winding
1758 bool altFlipped = false;
1759 bool foundFlipped = false;
1760 int foundMax = SK_MinS32;
1761 int foundSum = SK_MinS32;
1762 Segment* nextSegment;
1763 int lastNonZeroSum = winding;
1764 do {
1765 if (nextIndex == angleCount) {
1766 nextIndex = 0;
1767 }
1768 const Angle* nextAngle = sorted[nextIndex];
1769 nextSegment = nextAngle->segment();
1770 angleIsOp = nextSegment->operand();
1771 int sumWinding = angleIsOp ? bSumWinding : aSumWinding;
1772 int maxWinding = sumWinding;
1773 if (sumWinding) {
1774 lastNonZeroSum = sumWinding;
1775 }
1776 sumWinding -= nextSegment->spanSign(nextAngle);
1777 int xorMask = nextSegment->operand() ? bXorMask : aXorMask;
1778 bool otherNonZero;
1779 if (angleIsOp) {
1780 bSumWinding = sumWinding;
1781 otherNonZero = aSumWinding & aXorMask;
1782 } else {
1783 aSumWinding = sumWinding;
1784 otherNonZero = bSumWinding & bXorMask;
1785 }
1786 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00001787 #if 0 && DEBUG_WINDING
caryclark@google.com235f56a2012-09-14 14:19:30 +00001788 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
1789 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
1790 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
1791 #endif
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00001792
caryclark@google.com235f56a2012-09-14 14:19:30 +00001793 if (!(sumWinding & xorMask) && activeOp(angleIsOp, otherNonZero, op)) {
1794 if (!active) {
1795 markDone(SkMin32(startIndex, endIndex), outerWinding);
1796 // FIXME: seems like a bug that this isn't calling userInnerWinding
1797 nextSegment->markWinding(SkMin32(nextAngle->start(),
1798 nextAngle->end()), maxWinding);
1799 #if DEBUG_WINDING
1800 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
1801 #endif
1802 return NULL;
1803 }
1804 if (!foundAngle || foundDone) {
1805 foundAngle = nextAngle;
1806 foundDone = nextSegment->done(*nextAngle);
1807 foundFlipped = altFlipped;
1808 foundMax = maxWinding;
1809 }
1810 continue;
1811 }
1812 if (!(maxWinding & xorMask) && (!foundAngle || foundDone2)
1813 && activeOp(angleIsOp, otherNonZero, op)) {
1814 #if DEBUG_WINDING
1815 if (foundAngle && foundDone2) {
1816 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
1817 }
1818 #endif
1819 foundAngle = nextAngle;
1820 foundDone2 = nextSegment->done(*nextAngle);
1821 foundFlipped = altFlipped;
1822 foundSum = sumWinding;
1823 }
1824 if (nextSegment->done()) {
1825 continue;
1826 }
1827 // if the winding is non-zero, nextAngle does not connect to
1828 // current chain. If we haven't done so already, mark the angle
1829 // as done, record the winding value, and mark connected unambiguous
1830 // segments as well.
1831 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
1832 if (useInnerWinding(maxWinding, sumWinding)) {
1833 maxWinding = sumWinding;
1834 }
1835 Span* last;
1836 if (foundAngle) {
1837 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
1838 } else {
1839 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
1840 }
1841 if (last) {
1842 *chase.append() = last;
1843 }
1844 }
1845 } while (++nextIndex != lastIndex);
1846 markDone(SkMin32(startIndex, endIndex), outerWinding);
1847 if (!foundAngle) {
1848 return NULL;
1849 }
1850 nextStart = foundAngle->start();
1851 nextEnd = foundAngle->end();
1852 nextSegment = foundAngle->segment();
1853 int flipped = foundFlipped ? -1 : 1;
1854 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
1855 SkMin32(nextStart, nextEnd));
1856 if (winding) {
1857 #if DEBUG_WINDING
1858 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
1859 if (foundSum == SK_MinS32) {
1860 SkDebugf("?");
1861 } else {
1862 SkDebugf("%d", foundSum);
1863 }
1864 SkDebugf(" foundMax=");
1865 if (foundMax == SK_MinS32) {
1866 SkDebugf("?");
1867 } else {
1868 SkDebugf("%d", foundMax);
1869 }
1870 SkDebugf("\n");
1871 #endif
1872 winding = foundSum;
1873 }
1874 #if DEBUG_WINDING
1875 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
1876 #endif
1877 return nextSegment;
1878 }
caryclark@google.com47580692012-07-23 12:14:49 +00001879
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001880 // so the span needs to contain the pairing info found here
1881 // this should include the winding computed for the edge, and
1882 // what edge it connects to, and whether it is discarded
1883 // (maybe discarded == abs(winding) > 1) ?
1884 // only need derivatives for duration of sorting, add a new struct
1885 // for pairings, remove extra spans that have zero length and
1886 // reference an unused other
1887 // for coincident, the last span on the other may be marked done
1888 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001889
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001890 // if loop is exhausted, contour may be closed.
1891 // FIXME: pass in close point so we can check for closure
1892
1893 // given a segment, and a sense of where 'inside' is, return the next
1894 // segment. If this segment has an intersection, or ends in multiple
1895 // segments, find the mate that continues the outside.
1896 // note that if there are multiples, but no coincidence, we can limit
1897 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00001898
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001899 // mark found segments as done
1900
caryclark@google.com15fa1382012-05-07 20:49:36 +00001901 // start is the index of the beginning T of this edge
1902 // it is guaranteed to have an end which describes a non-zero length (?)
1903 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00001904 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001905 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
1906 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001907 const int startIndex = nextStart;
1908 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001909 int outerWinding = winding;
1910 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00001911 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001912 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1913 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00001914 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001915 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001916 outerWinding = innerWinding;
1917 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001918 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001919 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001920 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1921 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001922 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001923 #if PRECISE_T_SORT
1924 int end = nextExactSpan(startIndex, step);
1925 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001926 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001927 #endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001928 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001929 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001930 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001931 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001932 // mark the smaller of startIndex, endIndex done, and all adjacent
1933 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001934 #if DEBUG_WINDING
1935 SkDebugf("%s simple\n", __FUNCTION__);
1936 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001937 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001938 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001939 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00001940 double startT = other->fTs[nextStart].fT;
1941 nextEnd = nextStart;
1942 do {
1943 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001944 }
1945 #if PRECISE_T_SORT
1946 while (precisely_zero(startT - other->fTs[nextEnd].fT));
1947 #else
1948 while (approximately_zero(startT - other->fTs[nextEnd].fT));
1949 #endif
caryclark@google.com495f8e42012-05-31 13:13:11 +00001950 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001951 return other;
1952 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001953 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001954 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001955 SkASSERT(startIndex - endIndex != 0);
1956 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001957 addTwoAngles(startIndex, end, angles);
1958 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001959 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001960 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001961 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001962 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001963 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00001964 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001965 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001966 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001967 if (!sortable) {
1968 unsortable = true;
1969 return NULL;
1970 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001971 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001972 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001973 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00001974 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001975 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001976 int nextIndex = firstIndex + 1;
1977 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1978 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00001979 // FIXME: found done logic probably fails if there are more than 4
1980 // sorted angles. It should bias towards the first and last undone
1981 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001982 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00001983 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00001984 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001985 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00001986 bool altFlipped = false;
1987 bool foundFlipped = false;
1988 int foundMax = SK_MinS32;
1989 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001990 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00001991 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001992 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001993 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001994 nextIndex = 0;
1995 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001996 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00001997 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00001998 if (sumWinding) {
1999 lastNonZeroSum = sumWinding;
2000 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002001 nextSegment = nextAngle->segment();
caryclark@google.com2ddff932012-08-07 21:25:27 +00002002 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002003 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002004 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002005 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002006 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2007 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002008 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002009 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002010 if (!active) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002011 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002012 // FIXME: seems like a bug that this isn't calling userInnerWinding
caryclark@google.com47580692012-07-23 12:14:49 +00002013 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002014 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002015 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002016 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002017 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002018 return NULL;
2019 }
caryclark@google.com47580692012-07-23 12:14:49 +00002020 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002021 foundAngle = nextAngle;
caryclark@google.com47580692012-07-23 12:14:49 +00002022 foundDone = nextSegment->done(*nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002023 foundFlipped = altFlipped;
2024 foundMax = maxWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002025 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002026 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002027 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002028 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002029 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002030 if (foundAngle && foundDone2) {
2031 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002032 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002033 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002034 foundAngle = nextAngle;
caryclark@google.com24bec792012-08-20 12:43:57 +00002035 foundDone2 = nextSegment->done(*nextAngle);
2036 foundFlipped = altFlipped;
2037 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002038 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002039 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002040 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002041 }
2042 // if the winding is non-zero, nextAngle does not connect to
2043 // current chain. If we haven't done so already, mark the angle
2044 // as done, record the winding value, and mark connected unambiguous
2045 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002046 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002047 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002048 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002049 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002050 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002051 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002052 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002053 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002054 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002055 }
2056 if (last) {
2057 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002058 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002059 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002060 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002061 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002062 if (!foundAngle) {
2063 return NULL;
2064 }
2065 nextStart = foundAngle->start();
2066 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002067 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002068 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002069 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2070 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002071 if (winding) {
2072 #if DEBUG_WINDING
2073 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2074 if (foundSum == SK_MinS32) {
2075 SkDebugf("?");
2076 } else {
2077 SkDebugf("%d", foundSum);
2078 }
2079 SkDebugf(" foundMax=");
2080 if (foundMax == SK_MinS32) {
2081 SkDebugf("?");
2082 } else {
2083 SkDebugf("%d", foundMax);
2084 }
2085 SkDebugf("\n");
2086 #endif
2087 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002088 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002089 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002090 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002091 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002092 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002093 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002094
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002095 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002096 const int startIndex = nextStart;
2097 const int endIndex = nextEnd;
2098 SkASSERT(startIndex != endIndex);
2099 int count = fTs.count();
2100 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2101 : startIndex > 0);
2102 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002103 #if PRECISE_T_SORT
2104 int end = nextExactSpan(startIndex, step);
2105 #else
caryclark@google.com24bec792012-08-20 12:43:57 +00002106 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002107 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002108 SkASSERT(end >= 0);
2109 Span* endSpan = &fTs[end];
2110 Segment* other;
2111 markDone(SkMin32(startIndex, endIndex), 1);
2112 if (isSimple(end)) {
2113 #if DEBUG_WINDING
2114 SkDebugf("%s simple\n", __FUNCTION__);
2115 #endif
2116 other = endSpan->fOther;
2117 nextStart = endSpan->fOtherIndex;
2118 double startT = other->fTs[nextStart].fT;
2119 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002120 if ((approximately_less_than_zero(startT) && step < 0)
2121 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002122 step = -step;
2123 SkDEBUGCODE(firstLoop = false;)
2124 }
2125 do {
2126 nextEnd = nextStart;
2127 do {
2128 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002129 }
2130 #if PRECISE_T_SORT
2131 while (precisely_zero(startT - other->fTs[nextEnd].fT));
2132 #else
2133 while (approximately_zero(startT - other->fTs[nextEnd].fT));
2134 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002135 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2136 break;
2137 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002138 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002139 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002140 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002141 SkDEBUGCODE(firstLoop = false;)
2142 step = -step;
2143 } while (true);
2144 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2145 return other;
2146 }
2147 SkTDArray<Angle> angles;
2148 SkASSERT(startIndex - endIndex != 0);
2149 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2150 addTwoAngles(startIndex, end, angles);
2151 buildAngles(end, angles);
2152 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002153 bool sortable = SortAngles(angles, sorted);
caryclark@google.com24bec792012-08-20 12:43:57 +00002154 int angleCount = angles.count();
2155 int firstIndex = findStartingEdge(sorted, startIndex, end);
2156 SkASSERT(firstIndex >= 0);
2157 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002158 debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002159 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002160 if (!sortable) {
2161 unsortable = true;
2162 return NULL;
2163 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002164 SkASSERT(sorted[firstIndex]->segment() == this);
2165 int nextIndex = firstIndex + 1;
2166 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2167 const Angle* nextAngle;
2168 Segment* nextSegment;
2169 do {
2170 if (nextIndex == angleCount) {
2171 nextIndex = 0;
2172 }
2173 nextAngle = sorted[nextIndex];
2174 nextSegment = nextAngle->segment();
2175 if (!nextSegment->done(*nextAngle)) {
2176 break;
2177 }
2178 if (++nextIndex == lastIndex) {
2179 return NULL;
2180 }
2181 } while (true);
2182 nextStart = nextAngle->start();
2183 nextEnd = nextAngle->end();
2184 return nextSegment;
2185 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002186
2187 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2188 int angleCount = sorted.count();
2189 int firstIndex = -1;
2190 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2191 const Angle* angle = sorted[angleIndex];
2192 if (angle->segment() == this && angle->start() == end &&
2193 angle->end() == start) {
2194 firstIndex = angleIndex;
2195 break;
2196 }
2197 }
2198 return firstIndex;
2199 }
2200
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002201 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com235f56a2012-09-14 14:19:30 +00002202 void findTooCloseToCall(bool isXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002203 int count = fTs.count();
2204 if (count < 3) { // require t=0, x, 1 at minimum
2205 return;
2206 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002207 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002208 int moCount;
2209 Span* match;
2210 Segment* mOther;
2211 do {
2212 match = &fTs[matchIndex];
2213 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002214 // FIXME: allow quads, cubics to be near coincident?
2215 if (mOther->fVerb == SkPath::kLine_Verb) {
2216 moCount = mOther->fTs.count();
2217 if (moCount >= 3) {
2218 break;
2219 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002220 }
2221 if (++matchIndex >= count) {
2222 return;
2223 }
2224 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002225 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002226 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002227 // look for a pair of nearby T values that map to the same (x,y) value
2228 // if found, see if the pair of other segments share a common point. If
2229 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002230 for (int index = matchIndex + 1; index < count; ++index) {
2231 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002232 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002233 continue;
2234 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002235 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002236 if (tOther->fVerb != SkPath::kLine_Verb) {
2237 continue; // FIXME: allow quads, cubics to be near coincident?
2238 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002239 int toCount = tOther->fTs.count();
2240 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002241 continue;
2242 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002243 const SkPoint* testPt = &xyAtT(test);
2244 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002245 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002246 moCount = toCount;
2247 match = test;
2248 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002249 matchPt = testPt;
2250 continue;
2251 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002252 int moStart = -1;
2253 int moEnd = -1;
2254 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002255 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002256 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002257 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002258 continue;
2259 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002260 if (moSpan.fOther == this) {
2261 if (moSpan.fOtherT == match->fT) {
2262 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002263 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002264 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002265 continue;
2266 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002267 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002268 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2269 moStart = -1;
2270 break;
2271 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002272 SkASSERT(moEnd == -1);
2273 moEnd = moIndex;
2274 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002275 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002276 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002277 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002278 continue;
2279 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002280 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002281 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002282 continue;
2283 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002284 int toStart = -1;
2285 int toEnd = -1;
2286 double toStartT, toEndT;
2287 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2288 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002289 if (toSpan.fDone) {
2290 continue;
2291 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002292 if (toSpan.fOther == this) {
2293 if (toSpan.fOtherT == test->fT) {
2294 toStart = toIndex;
2295 toStartT = toSpan.fT;
2296 }
2297 continue;
2298 }
2299 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002300 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2301 moStart = -1;
2302 break;
2303 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002304 SkASSERT(toEnd == -1);
2305 toEnd = toIndex;
2306 toEndT = toSpan.fT;
2307 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002308 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002309 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2310 if (toStart <= 0 || toEnd <= 0) {
2311 continue;
2312 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002313 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002314 continue;
2315 }
2316 // test to see if the segment between there and here is linear
2317 if (!mOther->isLinear(moStart, moEnd)
2318 || !tOther->isLinear(toStart, toEnd)) {
2319 continue;
2320 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002321 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002322 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002323 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002324 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002325 // FIXME: this is bogus for multiple ops
2326 // the xorMask needs to be accumulated from the union of the two
2327 // edges -- which means that the segment must have its own copy of the mask
2328 mOther->addTCoincident(isXor, moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002329 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002330 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002331 }
2332
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002333 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002334 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002335 // a) mark spans with either end unsortable as done, or
2336 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2337 // when encountering an unsortable span
2338
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002339 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2340 // and use more concise logic like the old edge walker code?
2341 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002342 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002343 // iterate through T intersections and return topmost
2344 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002345 SkASSERT(!done());
2346 int firstT;
2347 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002348 SkPoint topPt;
2349 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002350 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002351 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002352 bool lastDone = true;
2353 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002354 const Span& span = fTs[index];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002355 if (!span.fDone || !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002356 const SkPoint& intercept = xyAtT(&span);
2357 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2358 && topPt.fX > intercept.fX)) {
2359 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002360 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002361 } else if (topPt == intercept) {
2362 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002363 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002364 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002365 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002366 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002367 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002368 int step = 1;
2369 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002370 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002371 step = -1;
2372 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002373 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002374 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002375 // if the topmost T is not on end, or is three-way or more, find left
2376 // look for left-ness from tLeft to firstT (matching y of other)
2377 SkTDArray<Angle> angles;
2378 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002379 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002380 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002381 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002382 (void) SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002383 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00002384 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002385 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002386 // skip edges that have already been processed
2387 firstT = -1;
2388 Segment* leftSegment;
2389 do {
2390 const Angle* angle = sorted[++firstT];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002391 if (angle->unsortable()) {
2392 // FIXME: if all angles are unsortable, find next topmost
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002393 if (firstT >= angles.count() - 1) {
2394 #if SORTABLE_CONTOURS
2395 SkASSERT(0);
2396 #endif
2397 return NULL;
2398 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002399 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002400 leftSegment = angle->segment();
2401 tIndex = angle->end();
2402 endIndex = angle->start();
2403 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002404 return leftSegment;
2405 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002406
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002407 // FIXME: not crazy about this
2408 // when the intersections are performed, the other index is into an
2409 // incomplete array. as the array grows, the indices become incorrect
2410 // while the following fixes the indices up again, it isn't smart about
2411 // skipping segments whose indices are already correct
2412 // assuming we leave the code that wrote the index in the first place
2413 void fixOtherTIndex() {
2414 int iCount = fTs.count();
2415 for (int i = 0; i < iCount; ++i) {
2416 Span& iSpan = fTs[i];
2417 double oT = iSpan.fOtherT;
2418 Segment* other = iSpan.fOther;
2419 int oCount = other->fTs.count();
2420 for (int o = 0; o < oCount; ++o) {
2421 Span& oSpan = other->fTs[o];
2422 if (oT == oSpan.fT && this == oSpan.fOther) {
2423 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002424 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002425 }
2426 }
2427 }
2428 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002429
caryclark@google.com495f8e42012-05-31 13:13:11 +00002430 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002431 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002432 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002433 SkASSERT(end >= 0);
2434 if (multipleSpans(end)) {
2435 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002436 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002437 const Span& endSpan = fTs[end];
2438 Segment* other = endSpan.fOther;
2439 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002440 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002441 Span* last = other->innerChaseDone(index, step, winding);
2442 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002443 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002444 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002445
caryclark@google.com59823f72012-08-09 18:17:47 +00002446 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002447 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002448 SkASSERT(end >= 0);
2449 if (multipleSpans(end)) {
2450 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002451 }
2452 const Span& endSpan = fTs[end];
2453 Segment* other = endSpan.fOther;
2454 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002455 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002456 int min = SkMin32(index, otherEnd);
2457 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002458 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002459 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002460 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002461 Span* last = other->innerChaseWinding(index, step, winding);
2462 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002463 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002464 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002465
caryclark@google.com235f56a2012-09-14 14:19:30 +00002466 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2467 fDoneSpans = 0;
2468 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002469 fPts = pts;
2470 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002471 }
2472
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002473 bool intersected() const {
2474 return fTs.count() > 0;
2475 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002476
2477 bool isConnected(int startIndex, int endIndex) const {
2478 return fTs[startIndex].fWindSum != SK_MinS32
2479 || fTs[endIndex].fWindSum != SK_MinS32;
2480 }
2481
caryclark@google.com235f56a2012-09-14 14:19:30 +00002482 bool isHorizontal() const {
2483 return fBounds.fTop == fBounds.fBottom;
2484 }
2485
caryclark@google.com15fa1382012-05-07 20:49:36 +00002486 bool isLinear(int start, int end) const {
2487 if (fVerb == SkPath::kLine_Verb) {
2488 return true;
2489 }
2490 if (fVerb == SkPath::kQuad_Verb) {
2491 SkPoint qPart[3];
2492 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2493 return QuadIsLinear(qPart);
2494 } else {
2495 SkASSERT(fVerb == SkPath::kCubic_Verb);
2496 SkPoint cPart[4];
2497 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2498 return CubicIsLinear(cPart);
2499 }
2500 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002501
2502 // OPTIMIZE: successive calls could start were the last leaves off
2503 // or calls could specialize to walk forwards or backwards
2504 bool isMissing(double startT) const {
2505 size_t tCount = fTs.count();
2506 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002507 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002508 return false;
2509 }
2510 }
2511 return true;
2512 }
2513
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002514 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002515 int count = fTs.count();
2516 if (count == 2) {
2517 return true;
2518 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002519 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002520 if (approximately_less_than_zero(t)) {
2521 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002522 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002523 if (approximately_greater_than_one(t)) {
2524 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002525 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002526 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002527 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002528
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002529 bool isVertical() const {
2530 return fBounds.fLeft == fBounds.fRight;
2531 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002532
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002533 SkScalar leftMost(int start, int end) const {
2534 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2535 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002536
caryclark@google.com495f8e42012-05-31 13:13:11 +00002537 // this span is excluded by the winding rule -- chase the ends
2538 // as long as they are unambiguous to mark connections as done
2539 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002540 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002541 int index = angle->start();
2542 int endIndex = angle->end();
2543 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002544 Span* last = innerChaseDone(index, step, winding);
2545 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002546 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002547 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002548
caryclark@google.com59823f72012-08-09 18:17:47 +00002549 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002550 int index = angle->start();
2551 int endIndex = angle->end();
2552 int min = SkMin32(index, endIndex);
2553 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002554 Span* last = innerChaseWinding(index, step, winding);
2555 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002556 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002557 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002558
caryclark@google.com495f8e42012-05-31 13:13:11 +00002559 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002560 // This may be called when the segment is already marked done. While this
2561 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002562 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002563 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002564 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002565 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002566 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002567 double referenceT = fTs[index].fT;
2568 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002569 #if PRECISE_T_SORT
2570 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2571 markOneDone(__FUNCTION__, lesser, winding);
2572 }
2573 do {
2574 markOneDone(__FUNCTION__, index, winding);
2575 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2576 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002577 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002578 markOneDone(__FUNCTION__, lesser, winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002579 }
2580 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002581 markOneDone(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002582 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00002583 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002584 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002585
caryclark@google.com24bec792012-08-20 12:43:57 +00002586 void markOneDone(const char* funName, int tIndex, int winding) {
2587 Span* span = markOneWinding(funName, tIndex, winding);
2588 if (!span) {
2589 return;
2590 }
2591 span->fDone = true;
2592 fDoneSpans++;
2593 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002594
caryclark@google.com24bec792012-08-20 12:43:57 +00002595 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2596 Span& span = fTs[tIndex];
2597 if (span.fDone) {
2598 return NULL;
2599 }
2600 #if DEBUG_MARK_DONE
2601 debugShowNewWinding(funName, span, winding);
2602 #endif
2603 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002604 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002605 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002606 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002607 span.fWindSum = winding;
2608 return &span;
2609 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002610
2611 void markUnsortable(int start, int end) {
2612 Span* span = &fTs[start];
2613 if (start < end) {
2614 span->fUnsortableStart = true;
2615 } else {
2616 --span;
2617 span->fUnsortableEnd = true;
2618 }
2619 if (span->fDone) {
2620 return;
2621 }
2622 span->fDone = true;
2623 fDoneSpans++;
2624 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002625
caryclark@google.com59823f72012-08-09 18:17:47 +00002626 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002627 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002628 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002629 double referenceT = fTs[index].fT;
2630 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002631 #if PRECISE_T_SORT
2632 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2633 markOneWinding(__FUNCTION__, lesser, winding);
2634 }
2635 do {
2636 markOneWinding(__FUNCTION__, index, winding);
2637 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2638 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002639 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002640 markOneWinding(__FUNCTION__, lesser, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002641 }
2642 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002643 markOneWinding(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002644 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00002645 #endif
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002646 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002647
caryclark@google.com2ddff932012-08-07 21:25:27 +00002648 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002649 int nextDoorWind = SK_MaxS32;
2650 if (tIndex > 0) {
2651 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002652 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002653 nextDoorWind = below.fWindValue;
2654 }
2655 }
2656 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2657 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002658 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002659 nextDoorWind = above.fWindValue;
2660 }
2661 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002662 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2663 const Span& below = fTs[tIndex - 1];
2664 nextDoorWind = below.fWindValue;
2665 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002666 if (nextDoorWind != SK_MaxS32) {
2667 Span& newSpan = fTs[tIndex];
2668 newSpan.fWindValue = nextDoorWind;
2669 if (!nextDoorWind) {
2670 newSpan.fDone = true;
2671 ++fDoneSpans;
2672 }
2673 }
2674 }
2675
caryclark@google.com9764cc62012-07-12 19:29:45 +00002676 // return span if when chasing, two or more radiating spans are not done
2677 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2678 // candidate and the remaining spans have windValue == 0 (canceled by
2679 // coincidence). The coincident edges could either be removed altogether,
2680 // or this code could be more complicated in detecting this case. Worth it?
2681 bool multipleSpans(int end) const {
2682 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002683 }
2684
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002685 // This has callers for two different situations: one establishes the end
2686 // of the current span, and one establishes the beginning of the next span
2687 // (thus the name). When this is looking for the end of the current span,
2688 // coincidence is found when the beginning Ts contain -step and the end
2689 // contains step. When it is looking for the beginning of the next, the
2690 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002691 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002692 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002693 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002694 int count = fTs.count();
2695 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002696 while (step > 0 ? ++to < count : --to >= 0) {
2697 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002698 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002699 continue;
2700 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002701 return to;
2702 }
2703 return -1;
2704 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00002705
caryclark@google.coma461ff02012-10-11 12:54:23 +00002706#if PRECISE_T_SORT
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002707 // FIXME
2708 // this returns at any difference in T, vs. a preset minimum. It may be
2709 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002710 // OPTIMIZATION splitting this into separate loops for up/down steps
2711 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002712 int nextExactSpan(int from, int step) const {
2713 const Span& fromSpan = fTs[from];
2714 int count = fTs.count();
2715 int to = from;
2716 while (step > 0 ? ++to < count : --to >= 0) {
2717 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00002718 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002719 continue;
2720 }
2721 return to;
2722 }
2723 return -1;
2724 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002725#endif
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002726
caryclark@google.com235f56a2012-09-14 14:19:30 +00002727 bool operand() const {
2728 return fOperand;
2729 }
2730
2731 int oppSign(int startIndex, int endIndex) const {
2732 int result = startIndex < endIndex ? -fTs[startIndex].fWindValueOpp :
2733 fTs[endIndex].fWindValueOpp;
2734#if DEBUG_WIND_BUMP
2735 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2736#endif
2737 return result;
2738 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002739
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002740 const SkPoint* pts() const {
2741 return fPts;
2742 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002743
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002744 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002745 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002746 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2747 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002748 }
2749
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002750 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
2751 int angleCount = angles.count();
2752 int angleIndex;
2753 angleList.setReserve(angleCount);
2754 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2755 *angleList.append() = &angles[angleIndex];
2756 }
2757 QSort<Angle>(angleList.begin(), angleList.end() - 1);
2758 bool result = true;
2759 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2760 Angle& angle = angles[angleIndex];
2761 if (angle.unsortable()) {
2762 // so that it is available for early exclusion in findTop and others
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002763 angle.segment()->markUnsortable(angle.start(), angle.end());
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002764 result = false;
2765 }
2766 }
2767 return result;
2768 }
2769
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002770 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002771 const Span& span(int tIndex) const {
2772 return fTs[tIndex];
2773 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002774
caryclark@google.com235f56a2012-09-14 14:19:30 +00002775 int spanSign(const Angle* angle) const {
2776 SkASSERT(angle->segment() == this);
2777 return spanSign(angle->start(), angle->end());
2778 }
2779
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002780 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002781 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue :
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002782 fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002783#if DEBUG_WIND_BUMP
2784 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2785#endif
2786 return result;
2787 }
2788
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002789 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002790 double t(int tIndex) const {
2791 return fTs[tIndex].fT;
2792 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002793
caryclark@google.com18063442012-07-25 12:05:18 +00002794 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
2795 double start) {
2796 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002797 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00002798 *outsideTs.append() = end;
2799 *outsideTs.append() = start;
2800 }
2801 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002802
caryclark@google.com24bec792012-08-20 12:43:57 +00002803 void undoneSpan(int& start, int& end) {
2804 size_t tCount = fTs.count();
2805 size_t index;
2806 for (index = 0; index < tCount; ++index) {
2807 if (!fTs[index].fDone) {
2808 break;
2809 }
2810 }
2811 SkASSERT(index < tCount - 1);
2812 start = index;
2813 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002814 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00002815 SkASSERT(index < tCount);
2816 SkASSERT(index < tCount);
2817 end = index;
2818 }
caryclark@google.com18063442012-07-25 12:05:18 +00002819
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002820 bool unsortable(int index) const {
2821 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
2822 }
2823
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002824 void updatePts(const SkPoint pts[]) {
2825 fPts = pts;
2826 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002827
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002828 SkPath::Verb verb() const {
2829 return fVerb;
2830 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002831
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002832 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002833 return fTs[tIndex].fWindSum;
2834 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002835
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002836 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002837 int start = angle->start();
2838 int end = angle->end();
2839 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002840 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002841 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002842
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002843 int windValue(int tIndex) const {
2844 return fTs[tIndex].fWindValue;
2845 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002846
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002847 int windValue(const Angle* angle) const {
2848 int start = angle->start();
2849 int end = angle->end();
2850 int index = SkMin32(start, end);
2851 return windValue(index);
2852 }
2853
2854 SkScalar xAtT(const Span* span) const {
2855 return xyAtT(span).fX;
2856 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002857
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002858 const SkPoint& xyAtT(int index) const {
2859 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002860 }
2861
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002862 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002863 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002864 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002865 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002866 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002867 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002868 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002869 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002870 }
2871 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002872 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002873 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002874
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002875 SkScalar yAtT(int index) const {
2876 return yAtT(&fTs[index]);
2877 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002878
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002879 SkScalar yAtT(const Span* span) const {
2880 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002881 }
2882
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002883#if DEBUG_DUMP
2884 void dump() const {
2885 const char className[] = "Segment";
2886 const int tab = 4;
2887 for (int i = 0; i < fTs.count(); ++i) {
2888 SkPoint out;
2889 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
2890 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002891 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002892 tab + sizeof(className), className, fID,
2893 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002894 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002895 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002896 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002897 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00002898 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002899 }
2900#endif
2901
caryclark@google.com47580692012-07-23 12:14:49 +00002902#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00002903 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002904 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00002905 for (int i = 0; i < fTs.count(); ++i) {
2906 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
2907 return;
2908 }
2909 }
2910 SkASSERT(0);
2911 }
2912#endif
2913
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002914#if DEBUG_DUMP
2915 int debugID() const {
2916 return fID;
2917 }
2918#endif
2919
caryclark@google.com24bec792012-08-20 12:43:57 +00002920#if DEBUG_WINDING
2921 void debugShowSums() const {
2922 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
2923 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
2924 for (int i = 0; i < fTs.count(); ++i) {
2925 const Span& span = fTs[i];
2926 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
2927 if (span.fWindSum == SK_MinS32) {
2928 SkDebugf("?");
2929 } else {
2930 SkDebugf("%d", span.fWindSum);
2931 }
2932 SkDebugf("]");
2933 }
2934 SkDebugf("\n");
2935 }
2936#endif
2937
caryclark@google.comcc905052012-07-25 20:59:42 +00002938#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002939 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00002940 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00002941 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00002942 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00002943 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
2944 }
2945 SkDebugf("\n");
2946 }
2947#endif
2948
caryclark@google.com027de222012-07-12 12:52:50 +00002949#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002950 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00002951 if (done()) {
2952 return;
2953 }
2954 for (int i = 0; i < fTs.count(); ++i) {
2955 if (fTs[i].fDone) {
2956 continue;
2957 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002958 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00002959 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2960 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2961 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2962 }
2963 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00002964 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00002965 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00002966 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002967 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
2968 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
2969 if (fTs[i].fWindSum == SK_MinS32) {
2970 SkDebugf("?");
2971 } else {
2972 SkDebugf("%d", fTs[i].fWindSum);
2973 }
2974 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00002975 }
2976 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002977
2978 // This isn't useful yet -- but leaving it in for now in case i think of something
2979 // to use it for
2980 void validateActiveSpans() const {
2981 if (done()) {
2982 return;
2983 }
2984 int tCount = fTs.count();
2985 for (int index = 0; index < tCount; ++index) {
2986 if (fTs[index].fDone) {
2987 continue;
2988 }
2989 // count number of connections which are not done
2990 int first = index;
2991 double baseT = fTs[index].fT;
2992 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
2993 --first;
2994 }
2995 int last = index;
2996 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
2997 ++last;
2998 }
2999 int connections = 0;
3000 connections += first > 0 && !fTs[first - 1].fDone;
3001 for (int test = first; test <= last; ++test) {
3002 connections += !fTs[test].fDone;
3003 const Segment* other = fTs[test].fOther;
3004 int oIndex = fTs[test].fOtherIndex;
3005 connections += !other->fTs[oIndex].fDone;
3006 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3007 }
3008 // SkASSERT(!(connections & 1));
3009 }
3010 }
caryclark@google.com027de222012-07-12 12:52:50 +00003011#endif
3012
caryclark@google.com0c803d02012-08-06 11:15:47 +00003013#if DEBUG_MARK_DONE
3014 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3015 const SkPoint& pt = xyAtT(&span);
3016 SkDebugf("%s id=%d", fun, fID);
3017 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3018 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3019 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3020 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003021 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3022 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3023 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3024 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003025 if (span.fWindSum == SK_MinS32) {
3026 SkDebugf("?");
3027 } else {
3028 SkDebugf("%d", span.fWindSum);
3029 }
3030 SkDebugf(" windValue=%d\n", span.fWindValue);
3031 }
3032#endif
3033
caryclark@google.com47580692012-07-23 12:14:49 +00003034#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003035 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003036 const int contourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003037 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003038 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003039 int lastSum = contourWinding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003040 int windSum = lastSum - spanSign(angles[first]);
caryclark@google.com03f97062012-08-21 13:13:52 +00003041 SkDebugf("%s %s contourWinding=%d sign=%d\n", fun, __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +00003042 contourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003043 int index = first;
3044 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003045 do {
3046 const Angle& angle = *angles[index];
3047 const Segment& segment = *angle.segment();
3048 int start = angle.start();
3049 int end = angle.end();
3050 const Span& sSpan = segment.fTs[start];
3051 const Span& eSpan = segment.fTs[end];
3052 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003053 if (!firstTime) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003054 lastSum = windSum;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003055 windSum -= segment.spanSign(&angle);
caryclark@google.comafe56de2012-07-24 18:11:03 +00003056 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003057 SkDebugf("%s [%d] %sid=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
3058 " sign=%d windValue=%d windSum=",
3059 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003060 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003061 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3062 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3063 mSpan.fWindValue);
3064 if (mSpan.fWindSum == SK_MinS32) {
3065 SkDebugf("?");
3066 } else {
3067 SkDebugf("%d", mSpan.fWindSum);
3068 }
3069 SkDebugf(" winding: %d->%d (max=%d) done=%d\n", lastSum, windSum,
3070 useInnerWinding(lastSum, windSum) ? windSum : lastSum,
3071 mSpan.fDone);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003072#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003073 angle.debugShow(segment.xyAtT(&sSpan));
3074#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003075 ++index;
3076 if (index == angles.count()) {
3077 index = 0;
3078 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003079 if (firstTime) {
3080 firstTime = false;
3081 }
caryclark@google.com47580692012-07-23 12:14:49 +00003082 } while (index != first);
3083 }
3084#endif
3085
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003086#if DEBUG_WINDING
3087 bool debugVerifyWinding(int start, int end, int winding) const {
3088 const Span& span = fTs[SkMin32(start, end)];
3089 int spanWinding = span.fWindSum;
3090 if (spanWinding == SK_MinS32) {
3091 return true;
3092 }
3093 int spanSign = SkSign32(start - end);
3094 int signedVal = spanSign * span.fWindValue;
3095 if (signedVal < 0) {
3096 spanWinding -= signedVal;
3097 }
3098 return span.fWindSum == winding;
3099 }
3100#endif
3101
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003102private:
3103 const SkPoint* fPts;
3104 SkPath::Verb fVerb;
3105 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003106 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00003107 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00003108 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003109#if DEBUG_DUMP
3110 int fID;
3111#endif
3112};
3113
caryclark@google.comb9738012012-07-03 19:53:30 +00003114class Contour;
3115
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003116struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003117 Contour* fContours[2];
3118 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003119 double fTs[2][2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00003120 bool fXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003121};
3122
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003123class Contour {
3124public:
3125 Contour() {
3126 reset();
3127#if DEBUG_DUMP
3128 fID = ++gContourID;
3129#endif
3130 }
3131
3132 bool operator<(const Contour& rh) const {
3133 return fBounds.fTop == rh.fBounds.fTop
3134 ? fBounds.fLeft < rh.fBounds.fLeft
3135 : fBounds.fTop < rh.fBounds.fTop;
3136 }
3137
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003138 void addCoincident(int index, Contour* other, int otherIndex,
3139 const Intersections& ts, bool swap) {
3140 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00003141 coincidence.fContours[0] = this;
3142 coincidence.fContours[1] = other;
3143 coincidence.fSegments[0] = index;
3144 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003145 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3146 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3147 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3148 coincidence.fTs[swap][0] = ts.fT[0][0];
3149 coincidence.fTs[swap][1] = ts.fT[0][1];
3150 coincidence.fTs[!swap][0] = ts.fT[1][0];
3151 coincidence.fTs[!swap][1] = ts.fT[1][1];
3152 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3153 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3154 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3155 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3156 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3157 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3158 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003159 coincidence.fXor = fOperand == other->fOperand ? fXor : true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003160 }
3161
3162 void addCross(const Contour* crosser) {
3163#ifdef DEBUG_CROSS
3164 for (int index = 0; index < fCrosses.count(); ++index) {
3165 SkASSERT(fCrosses[index] != crosser);
3166 }
3167#endif
3168 *fCrosses.append() = crosser;
3169 }
3170
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003171 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003172 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003173 fContainsCurves = true;
3174 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003175
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003176 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003177 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003178 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003179 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003180
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003181 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3182 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3183 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003184
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003185 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003186 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003187 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003188 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003189 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003190
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003191 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3192 containsIntercepts();
3193 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3194 }
3195
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003196 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003197 return fBounds;
3198 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003199
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003200 void collapseTriangles() {
3201 int segmentCount = fSegments.count();
3202 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3203 fSegments[sIndex].collapseTriangles(fXor);
3204 }
3205 }
3206
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003207 void complete() {
3208 setBounds();
3209 fContainsIntercepts = false;
3210 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003211
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003212 void containsIntercepts() {
3213 fContainsIntercepts = true;
3214 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003215
rmistry@google.comd6176b02012-08-23 18:14:13 +00003216 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003217 int &tIndex, double& hitT) {
3218 int segmentCount = fSegments.count();
3219 const Segment* bestSegment = NULL;
3220 for (int test = 0; test < segmentCount; ++test) {
3221 Segment* testSegment = &fSegments[test];
3222 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003223 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003224 continue;
3225 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003226 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003227 continue;
3228 }
3229 if (bounds.fLeft > basePt.fX) {
3230 continue;
3231 }
3232 if (bounds.fRight < basePt.fX) {
3233 continue;
3234 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003235 if (bounds.fLeft == bounds.fRight) {
3236 continue;
3237 }
3238 #if 0
3239 bool leftHalf = bounds.fLeft == basePt.fX;
3240 bool rightHalf = bounds.fRight == basePt.fX;
3241 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3242 basePt, leftHalf, rightHalf)) {
3243 continue;
3244 }
3245 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003246 double testHitT;
3247 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3248 if (testT >= 0) {
3249 bestSegment = testSegment;
3250 tIndex = testT;
3251 hitT = testHitT;
3252 }
3253 }
3254 return bestSegment;
3255 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003256
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003257 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003258 for (int index = 0; index < fCrosses.count(); ++index) {
3259 if (fCrosses[index] == crosser) {
3260 return true;
3261 }
3262 }
3263 return false;
3264 }
3265
caryclark@google.com235f56a2012-09-14 14:19:30 +00003266 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003267 int segmentCount = fSegments.count();
3268 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003269 fSegments[sIndex].findTooCloseToCall(fXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003270 }
3271 }
3272
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003273 void fixOtherTIndex() {
3274 int segmentCount = fSegments.count();
3275 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3276 fSegments[sIndex].fixOtherTIndex();
3277 }
3278 }
3279
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003280 void reset() {
3281 fSegments.reset();
3282 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003283 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003284 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003285
caryclark@google.com235f56a2012-09-14 14:19:30 +00003286 // FIXME: for binary ops, need to keep both ops winding contributions separately
3287 // in edge array
3288 void resolveCoincidence() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003289 int count = fCoincidences.count();
3290 for (int index = 0; index < count; ++index) {
3291 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003292 Contour* thisContour = coincidence.fContours[0];
3293 Contour* otherContour = coincidence.fContours[1];
3294 int thisIndex = coincidence.fSegments[0];
3295 int otherIndex = coincidence.fSegments[1];
3296 Segment& thisOne = thisContour->fSegments[thisIndex];
3297 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003298 #if DEBUG_CONCIDENT
3299 thisOne.debugShowTs();
3300 other.debugShowTs();
3301 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003302 double startT = coincidence.fTs[0][0];
3303 double endT = coincidence.fTs[0][1];
caryclark@google.com32546db2012-08-31 20:55:07 +00003304 bool opposite = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003305 if (startT > endT) {
3306 SkTSwap<double>(startT, endT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003307 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003308 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003309 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003310 double oStartT = coincidence.fTs[1][0];
3311 double oEndT = coincidence.fTs[1][1];
3312 if (oStartT > oEndT) {
3313 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003314 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003315 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003316 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com32546db2012-08-31 20:55:07 +00003317 if (opposite) {
caryclark@google.comb9738012012-07-03 19:53:30 +00003318 // make sure startT and endT have t entries
caryclark@google.com32546db2012-08-31 20:55:07 +00003319 SkASSERT(opposite);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003320 if (startT > 0 || oEndT < 1
3321 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3322 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003323 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003324 if (oStartT > 0 || endT < 1
3325 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3326 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003327 }
3328 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003329 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003330 if (startT > 0 || oStartT > 0
3331 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003332 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003333 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003334 if (endT < 1 || oEndT < 1
3335 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003336 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003337 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003338 thisOne.addTCoincident(coincidence.fXor, startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003339 }
caryclark@google.com47580692012-07-23 12:14:49 +00003340 #if DEBUG_CONCIDENT
3341 thisOne.debugShowTs();
3342 other.debugShowTs();
3343 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003344 }
3345 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003346
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003347 const SkTArray<Segment>& segments() {
3348 return fSegments;
3349 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003350
caryclark@google.com235f56a2012-09-14 14:19:30 +00003351 void setOperand(bool isOp) {
3352 fOperand = isOp;
3353 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003354
caryclark@google.com235f56a2012-09-14 14:19:30 +00003355 void setXor(bool isXor) {
3356 fXor = isXor;
3357 }
3358
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003359#if !SORTABLE_CONTOURS
3360 void sortSegments() {
3361 int segmentCount = fSegments.count();
3362 fSortedSegments.setReserve(segmentCount);
3363 for (int test = 0; test < segmentCount; ++test) {
3364 *fSortedSegments.append() = &fSegments[test];
3365 }
3366 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
3367 fFirstSorted = 0;
3368 }
3369#endif
3370
caryclark@google.com15fa1382012-05-07 20:49:36 +00003371 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3372 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003373 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003374 // segments' top, and not a true sort, so it could be ameniable to regular
3375 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003376 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003377 int segmentCount = fSegments.count();
3378 SkASSERT(segmentCount > 0);
3379 int best = -1;
3380 Segment* bestSegment = NULL;
3381 while (++best < segmentCount) {
3382 Segment* testSegment = &fSegments[best];
3383 if (testSegment->done()) {
3384 continue;
3385 }
3386 bestSegment = testSegment;
3387 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003388 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003389 if (!bestSegment) {
3390 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003391 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003392 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003393 for (int test = best + 1; test < segmentCount; ++test) {
3394 Segment* testSegment = &fSegments[test];
3395 if (testSegment->done()) {
3396 continue;
3397 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003398 if (testSegment->bounds().fTop > bestTop) {
3399 continue;
3400 }
3401 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003402 if (bestTop > testTop) {
3403 bestTop = testTop;
3404 bestSegment = testSegment;
3405 }
3406 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003407 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003408 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003409 }
3410
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003411#if !SORTABLE_CONTOURS
3412 Segment* topSortableSegment(SkScalar& bestY) {
3413 int segmentCount = fSortedSegments.count();
3414 SkASSERT(segmentCount > 0);
3415 Segment* bestSegment = NULL;
3416 while (fFirstSorted < segmentCount) {
3417 Segment* testSegment = fSortedSegments[fFirstSorted];
3418 if (testSegment->done()) {
3419 fFirstSorted++;
3420 continue;
3421 }
3422 bestSegment = testSegment;
3423 break;
3424 }
3425 if (!bestSegment) {
3426 return NULL;
3427 }
3428 bestY = bestSegment->activeTop();
3429 return bestSegment;
3430 }
3431#endif
3432
caryclark@google.com24bec792012-08-20 12:43:57 +00003433 Segment* undoneSegment(int& start, int& end) {
3434 int segmentCount = fSegments.count();
3435 for (int test = 0; test < segmentCount; ++test) {
3436 Segment* testSegment = &fSegments[test];
3437 if (testSegment->done()) {
3438 continue;
3439 }
3440 testSegment->undoneSpan(start, end);
3441 return testSegment;
3442 }
3443 return NULL;
3444 }
3445
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003446 int updateSegment(int index, const SkPoint* pts) {
3447 Segment& segment = fSegments[index];
3448 segment.updatePts(pts);
3449 return segment.verb() + 1;
3450 }
3451
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003452#if DEBUG_TEST
3453 SkTArray<Segment>& debugSegments() {
3454 return fSegments;
3455 }
3456#endif
3457
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003458#if DEBUG_DUMP
3459 void dump() {
3460 int i;
3461 const char className[] = "Contour";
3462 const int tab = 4;
3463 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
3464 for (i = 0; i < fSegments.count(); ++i) {
3465 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
3466 className, i);
3467 fSegments[i].dump();
3468 }
3469 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
3470 tab + sizeof(className), className,
3471 fBounds.fLeft, fBounds.fTop,
3472 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003473 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
3474 className, fContainsIntercepts);
3475 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
3476 className, fContainsCurves);
3477 }
3478#endif
3479
caryclark@google.com027de222012-07-12 12:52:50 +00003480#if DEBUG_ACTIVE_SPANS
3481 void debugShowActiveSpans() {
3482 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003483 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00003484 }
3485 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003486
3487 void validateActiveSpans() {
3488 for (int index = 0; index < fSegments.count(); ++index) {
3489 fSegments[index].validateActiveSpans();
3490 }
3491 }
caryclark@google.com027de222012-07-12 12:52:50 +00003492#endif
3493
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003494protected:
3495 void setBounds() {
3496 int count = fSegments.count();
3497 if (count == 0) {
3498 SkDebugf("%s empty contour\n", __FUNCTION__);
3499 SkASSERT(0);
3500 // FIXME: delete empty contour?
3501 return;
3502 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003503 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003504 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003505 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003506 }
3507 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003508
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003509private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003510 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003511#if !SORTABLE_CONTOURS
3512 SkTDArray<Segment*> fSortedSegments;
3513 int fFirstSorted;
3514#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003515 SkTDArray<Coincidence> fCoincidences;
3516 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003517 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003518 bool fContainsIntercepts;
3519 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003520 bool fOperand; // true for the second argument to a binary operator
3521 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003522#if DEBUG_DUMP
3523 int fID;
3524#endif
3525};
3526
3527class EdgeBuilder {
3528public:
3529
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003530EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003531 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003532 , fContours(contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003533 , fCurrentContour(NULL)
3534 , fOperand(false)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003535{
caryclark@google.com235f56a2012-09-14 14:19:30 +00003536 fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003537#if DEBUG_DUMP
3538 gContourID = 0;
3539 gSegmentID = 0;
3540#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00003541 fSecondHalf = preFetch();
3542}
3543
3544void addOperand(const SkPath& path) {
3545 fPath = &path;
3546 fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
3547 preFetch();
3548}
3549
3550void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003551 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003552 complete();
3553 if (fCurrentContour && !fCurrentContour->segments().count()) {
3554 fContours.pop_back();
3555 }
3556 // correct pointers in contours since fReducePts may have moved as it grew
3557 int cIndex = 0;
3558 int extraCount = fExtra.count();
3559 SkASSERT(extraCount == 0 || fExtra[0] == -1);
3560 int eIndex = 0;
3561 int rIndex = 0;
3562 while (++eIndex < extraCount) {
3563 int offset = fExtra[eIndex];
3564 if (offset < 0) {
3565 ++cIndex;
3566 continue;
3567 }
3568 fCurrentContour = &fContours[cIndex];
3569 rIndex += fCurrentContour->updateSegment(offset - 1,
3570 &fReducePts[rIndex]);
3571 }
3572 fExtra.reset(); // we're done with this
3573}
3574
3575ShapeOpMask xorMask() const {
3576 return fXorMask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003577}
3578
3579protected:
3580
3581void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003582 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003583 fCurrentContour->complete();
3584 fCurrentContour = NULL;
3585 }
3586}
3587
caryclark@google.com235f56a2012-09-14 14:19:30 +00003588// FIXME:remove once we can access path pts directly
3589int preFetch() {
3590 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003591 SkPoint pts[4];
3592 SkPath::Verb verb;
3593 do {
3594 verb = iter.next(pts);
3595 *fPathVerbs.append() = verb;
3596 if (verb == SkPath::kMove_Verb) {
3597 *fPathPts.append() = pts[0];
3598 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
3599 fPathPts.append(verb, &pts[1]);
3600 }
3601 } while (verb != SkPath::kDone_Verb);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003602 return fPathVerbs.count();
3603}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003604
caryclark@google.com235f56a2012-09-14 14:19:30 +00003605void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003606 SkPath::Verb reducedVerb;
3607 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003608 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003609 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003610 const SkPoint* finalCurveStart = NULL;
3611 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003612 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003613 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
3614 switch (verb) {
3615 case SkPath::kMove_Verb:
3616 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003617 if (!fCurrentContour) {
3618 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003619 fCurrentContour->setOperand(fOperand);
3620 fCurrentContour->setXor(fXorMask == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003621 *fExtra.append() = -1; // start new contour
3622 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003623 finalCurveEnd = pointsPtr++;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003624 continue;
3625 case SkPath::kLine_Verb:
3626 // skip degenerate points
3627 if (pointsPtr[-1].fX != pointsPtr[0].fX
3628 || pointsPtr[-1].fY != pointsPtr[0].fY) {
3629 fCurrentContour->addLine(&pointsPtr[-1]);
3630 }
3631 break;
3632 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00003633
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003634 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
3635 if (reducedVerb == 0) {
3636 break; // skip degenerate points
3637 }
3638 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00003639 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003640 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003641 break;
3642 }
3643 fCurrentContour->addQuad(&pointsPtr[-1]);
3644 break;
3645 case SkPath::kCubic_Verb:
3646 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
3647 if (reducedVerb == 0) {
3648 break; // skip degenerate points
3649 }
3650 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003651 *fExtra.append() =
3652 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003653 break;
3654 }
3655 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003656 *fExtra.append() =
3657 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003658 break;
3659 }
3660 fCurrentContour->addCubic(&pointsPtr[-1]);
3661 break;
3662 case SkPath::kClose_Verb:
3663 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003664 if (finalCurveStart && finalCurveEnd
3665 && *finalCurveStart != *finalCurveEnd) {
3666 *fReducePts.append() = *finalCurveStart;
3667 *fReducePts.append() = *finalCurveEnd;
3668 *fExtra.append() =
3669 fCurrentContour->addLine(fReducePts.end() - 2);
3670 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003671 complete();
3672 continue;
3673 default:
3674 SkDEBUGFAIL("bad verb");
3675 return;
3676 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003677 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003678 pointsPtr += verb;
3679 SkASSERT(fCurrentContour);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003680 if (verbPtr == endOfFirstHalf) {
3681 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003682 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003683 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003684}
3685
3686private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00003687 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003688 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003689 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003690 Contour* fCurrentContour;
3691 SkTArray<Contour>& fContours;
3692 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003693 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com235f56a2012-09-14 14:19:30 +00003694 ShapeOpMask fXorMask;
3695 int fSecondHalf;
3696 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003697};
3698
3699class Work {
3700public:
3701 enum SegmentType {
3702 kHorizontalLine_Segment = -1,
3703 kVerticalLine_Segment = 0,
3704 kLine_Segment = SkPath::kLine_Verb,
3705 kQuad_Segment = SkPath::kQuad_Verb,
3706 kCubic_Segment = SkPath::kCubic_Verb,
3707 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00003708
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003709 void addCoincident(Work& other, const Intersections& ts, bool swap) {
3710 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
3711 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003712
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003713 // FIXME: does it make sense to write otherIndex now if we're going to
3714 // fix it up later?
3715 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003716 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003717 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003718
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003719 // Avoid collapsing t values that are close to the same since
3720 // we walk ts to describe consecutive intersections. Since a pair of ts can
3721 // be nearly equal, any problems caused by this should be taken care
3722 // of later.
3723 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003724 int addT(double newT, const Work& other) {
3725 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003726 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003727
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003728 bool advance() {
3729 return ++fIndex < fLast;
3730 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003731
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003732 SkScalar bottom() const {
3733 return bounds().fBottom;
3734 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003735
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003736 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003737 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003738 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003739
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003740 const SkPoint* cubic() const {
3741 return fCubic;
3742 }
3743
3744 void init(Contour* contour) {
3745 fContour = contour;
3746 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003747 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003748 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003749
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003750 bool isAdjacent(const Work& next) {
3751 return fContour == next.fContour && fIndex + 1 == next.fIndex;
3752 }
3753
3754 bool isFirstLast(const Work& next) {
3755 return fContour == next.fContour && fIndex == 0
3756 && next.fIndex == fLast - 1;
3757 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003758
3759 SkScalar left() const {
3760 return bounds().fLeft;
3761 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003762
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003763 void promoteToCubic() {
3764 fCubic[0] = pts()[0];
3765 fCubic[2] = pts()[1];
3766 fCubic[3] = pts()[2];
3767 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
3768 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
3769 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
3770 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
3771 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003772
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003773 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003774 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003775 }
3776
3777 SkScalar right() const {
3778 return bounds().fRight;
3779 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003780
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003781 ptrdiff_t segmentIndex() const {
3782 return fIndex;
3783 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003784
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003785 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003786 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003787 SegmentType type = (SegmentType) segment.verb();
3788 if (type != kLine_Segment) {
3789 return type;
3790 }
3791 if (segment.isHorizontal()) {
3792 return kHorizontalLine_Segment;
3793 }
3794 if (segment.isVertical()) {
3795 return kVerticalLine_Segment;
3796 }
3797 return kLine_Segment;
3798 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003799
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003800 bool startAfter(const Work& after) {
3801 fIndex = after.fIndex;
3802 return advance();
3803 }
3804
3805 SkScalar top() const {
3806 return bounds().fTop;
3807 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003808
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003809 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003810 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003811 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003812
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003813 SkScalar x() const {
3814 return bounds().fLeft;
3815 }
3816
3817 bool xFlipped() const {
3818 return x() != pts()[0].fX;
3819 }
3820
3821 SkScalar y() const {
3822 return bounds().fTop;
3823 }
3824
3825 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003826 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003827 }
3828
3829protected:
3830 Contour* fContour;
3831 SkPoint fCubic[4];
3832 int fIndex;
3833 int fLast;
3834};
3835
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003836#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003837static void debugShowLineIntersection(int pts, const Work& wt,
3838 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003839 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003840 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003841 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
3842 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
3843 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
3844 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003845 return;
3846 }
3847 SkPoint wtOutPt, wnOutPt;
3848 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3849 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00003850 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003851 __FUNCTION__,
3852 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3853 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
3854 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00003855 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003856 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00003857 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003858 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3859 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
3860 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00003861 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
3862 }
3863 SkDebugf("\n");
3864}
3865
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003866static void debugShowQuadLineIntersection(int pts, const Work& wt,
3867 const Work& wn, const double wtTs[2], const double wnTs[2]) {
3868 if (!pts) {
3869 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
3870 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
3871 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
3872 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
3873 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
3874 wt.pts()[2].fX, wt.pts()[2].fY );
3875 return;
3876 }
3877 SkPoint wtOutPt, wnOutPt;
3878 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3879 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
3880 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
3881 __FUNCTION__,
3882 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3883 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
3884 wtOutPt.fX, wtOutPt.fY);
3885 if (pts == 2) {
3886 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
3887 }
3888 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
3889 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3890 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
3891 if (pts == 2) {
3892 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
3893 }
3894 SkDebugf("\n");
3895}
3896
caryclark@google.coma461ff02012-10-11 12:54:23 +00003897static void debugShowQuadIntersection(int pts, const Work& wt,
3898 const Work& wn, const double wtTs[2], const double wnTs[2]) {
3899 if (!pts) {
3900 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
3901 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
3902 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00003903 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00003904 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
3905 wt.pts()[2].fX, wt.pts()[2].fY );
3906 return;
3907 }
3908 SkPoint wtOutPt, wnOutPt;
3909 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3910 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
3911 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
3912 __FUNCTION__,
3913 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3914 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
3915 wtOutPt.fX, wtOutPt.fY);
3916 if (pts == 2) {
3917 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
3918 }
3919 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
3920 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3921 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
3922 wnOutPt.fX, wnOutPt.fY);
3923 if (pts == 2) {
3924 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003925 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003926 SkDebugf("\n");
3927}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003928#else
3929static void debugShowLineIntersection(int , const Work& ,
3930 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003931}
caryclark@google.coma461ff02012-10-11 12:54:23 +00003932
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003933static void debugShowQuadLineIntersection(int , const Work& ,
3934 const Work& , const double [2], const double [2]) {
3935}
3936
caryclark@google.coma461ff02012-10-11 12:54:23 +00003937static void debugShowQuadIntersection(int , const Work& ,
3938 const Work& , const double [2], const double [2]) {
3939}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003940#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003941
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003942static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003943
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003944 if (test != next) {
3945 if (test->bounds().fBottom < next->bounds().fTop) {
3946 return false;
3947 }
3948 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
3949 return true;
3950 }
3951 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003952 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003953 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003954 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003955 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003956 Work wn;
3957 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003958 if (test == next && !wn.startAfter(wt)) {
3959 continue;
3960 }
3961 do {
3962 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
3963 continue;
3964 }
3965 int pts;
3966 Intersections ts;
3967 bool swap = false;
3968 switch (wt.segmentType()) {
3969 case Work::kHorizontalLine_Segment:
3970 swap = true;
3971 switch (wn.segmentType()) {
3972 case Work::kHorizontalLine_Segment:
3973 case Work::kVerticalLine_Segment:
3974 case Work::kLine_Segment: {
3975 pts = HLineIntersect(wn.pts(), wt.left(),
3976 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003977 debugShowLineIntersection(pts, wt, wn,
3978 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003979 break;
3980 }
3981 case Work::kQuad_Segment: {
3982 pts = HQuadIntersect(wn.pts(), wt.left(),
3983 wt.right(), wt.y(), wt.xFlipped(), ts);
3984 break;
3985 }
3986 case Work::kCubic_Segment: {
3987 pts = HCubicIntersect(wn.pts(), wt.left(),
3988 wt.right(), wt.y(), wt.xFlipped(), ts);
3989 break;
3990 }
3991 default:
3992 SkASSERT(0);
3993 }
3994 break;
3995 case Work::kVerticalLine_Segment:
3996 swap = true;
3997 switch (wn.segmentType()) {
3998 case Work::kHorizontalLine_Segment:
3999 case Work::kVerticalLine_Segment:
4000 case Work::kLine_Segment: {
4001 pts = VLineIntersect(wn.pts(), wt.top(),
4002 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004003 debugShowLineIntersection(pts, wt, wn,
4004 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004005 break;
4006 }
4007 case Work::kQuad_Segment: {
4008 pts = VQuadIntersect(wn.pts(), wt.top(),
4009 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4010 break;
4011 }
4012 case Work::kCubic_Segment: {
4013 pts = VCubicIntersect(wn.pts(), wt.top(),
4014 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4015 break;
4016 }
4017 default:
4018 SkASSERT(0);
4019 }
4020 break;
4021 case Work::kLine_Segment:
4022 switch (wn.segmentType()) {
4023 case Work::kHorizontalLine_Segment:
4024 pts = HLineIntersect(wt.pts(), wn.left(),
4025 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004026 debugShowLineIntersection(pts, wt, wn,
4027 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004028 break;
4029 case Work::kVerticalLine_Segment:
4030 pts = VLineIntersect(wt.pts(), wn.top(),
4031 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004032 debugShowLineIntersection(pts, wt, wn,
4033 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004034 break;
4035 case Work::kLine_Segment: {
4036 pts = LineIntersect(wt.pts(), wn.pts(), ts);
4037 debugShowLineIntersection(pts, wt, wn,
4038 ts.fT[1], ts.fT[0]);
4039 break;
4040 }
4041 case Work::kQuad_Segment: {
4042 swap = true;
4043 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004044 debugShowQuadLineIntersection(pts, wt, wn,
4045 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004046 break;
4047 }
4048 case Work::kCubic_Segment: {
4049 swap = true;
4050 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
4051 break;
4052 }
4053 default:
4054 SkASSERT(0);
4055 }
4056 break;
4057 case Work::kQuad_Segment:
4058 switch (wn.segmentType()) {
4059 case Work::kHorizontalLine_Segment:
4060 pts = HQuadIntersect(wt.pts(), wn.left(),
4061 wn.right(), wn.y(), wn.xFlipped(), ts);
4062 break;
4063 case Work::kVerticalLine_Segment:
4064 pts = VQuadIntersect(wt.pts(), wn.top(),
4065 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4066 break;
4067 case Work::kLine_Segment: {
4068 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004069 debugShowQuadLineIntersection(pts, wt, wn,
4070 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004071 break;
4072 }
4073 case Work::kQuad_Segment: {
4074 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004075 debugShowQuadIntersection(pts, wt, wn,
4076 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004077 break;
4078 }
4079 case Work::kCubic_Segment: {
4080 wt.promoteToCubic();
4081 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
4082 break;
4083 }
4084 default:
4085 SkASSERT(0);
4086 }
4087 break;
4088 case Work::kCubic_Segment:
4089 switch (wn.segmentType()) {
4090 case Work::kHorizontalLine_Segment:
4091 pts = HCubicIntersect(wt.pts(), wn.left(),
4092 wn.right(), wn.y(), wn.xFlipped(), ts);
4093 break;
4094 case Work::kVerticalLine_Segment:
4095 pts = VCubicIntersect(wt.pts(), wn.top(),
4096 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4097 break;
4098 case Work::kLine_Segment: {
4099 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
4100 break;
4101 }
4102 case Work::kQuad_Segment: {
4103 wn.promoteToCubic();
4104 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
4105 break;
4106 }
4107 case Work::kCubic_Segment: {
4108 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4109 break;
4110 }
4111 default:
4112 SkASSERT(0);
4113 }
4114 break;
4115 default:
4116 SkASSERT(0);
4117 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004118 if (!foundCommonContour && pts > 0) {
4119 test->addCross(next);
4120 next->addCross(test);
4121 foundCommonContour = true;
4122 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004123 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004124 if (pts == 2) {
4125 if (wn.segmentType() <= Work::kLine_Segment
4126 && wt.segmentType() <= Work::kLine_Segment) {
4127 wt.addCoincident(wn, ts, swap);
4128 continue;
4129 }
4130 if (wn.segmentType() == Work::kQuad_Segment
4131 && wt.segmentType() == Work::kQuad_Segment
4132 && ts.coincidentUsed() == 2) {
4133 wt.addCoincident(wn, ts, swap);
4134 continue;
4135 }
4136
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004137 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004138 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004139 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4140 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004141 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4142 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004143 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4144 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004145 }
4146 } while (wn.advance());
4147 } while (wt.advance());
4148 return true;
4149}
4150
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004151// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004152// see if coincidence is formed by clipping non-concident segments
caryclark@google.com235f56a2012-09-14 14:19:30 +00004153static void coincidenceCheck(SkTDArray<Contour*>& contourList) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004154 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004155 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004156 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004157 contour->resolveCoincidence();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004158 }
4159 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4160 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004161 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004162 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004163#if 0
4164 // OPTIMIZATION: this check could be folded in with findTooClose -- maybe
4165 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4166 Contour* contour = contourList[cIndex];
4167 contour->collapseTriangles();
4168 }
4169#endif
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004170}
4171
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004172// project a ray from the top of the contour up and see if it hits anything
4173// note: when we compute line intersections, we keep track of whether
4174// two contours touch, so we need only look at contours not touching this one.
4175// OPTIMIZATION: sort contourList vertically to avoid linear walk
4176static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004177 const Segment* current, int index, int endIndex) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004178 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004179 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004180 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00004181 const Segment* test = NULL;
4182 int tIndex;
4183 double tHit;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004184 // bool checkCrosses = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004185 for (int cTest = 0; cTest < contourCount; ++cTest) {
4186 Contour* contour = contourList[cTest];
4187 if (basePt.fY < contour->bounds().fTop) {
4188 continue;
4189 }
4190 if (bestY > contour->bounds().fBottom) {
4191 continue;
4192 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004193#if 0
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004194 // even though the contours crossed, if spans cancel through concidence,
4195 // the contours may be not have any span links to chase, and the current
4196 // segment may be isolated. Detect this by seeing if current has
4197 // uninitialized wind sums. If so, project a ray instead of relying on
4198 // previously found intersections.
4199 if (baseContour == contour) {
4200 continue;
4201 }
4202 if (checkCrosses && baseContour->crosses(contour)) {
4203 if (current->isConnected(index, endIndex)) {
4204 continue;
4205 }
4206 checkCrosses = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004207 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004208#endif
caryclark@google.com47580692012-07-23 12:14:49 +00004209 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
4210 if (next) {
4211 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004212 }
caryclark@google.com47580692012-07-23 12:14:49 +00004213 }
4214 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00004215 return 0;
4216 }
4217 int winding, windValue;
4218 // If the ray hit the end of a span, we need to construct the wheel of
4219 // angles to find the span closest to the ray -- even if there are just
4220 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004221 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004222 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00004223 SkTDArray<Angle> angles;
4224 int end = test->nextSpan(tIndex, 1);
4225 if (end < 0) {
4226 end = test->nextSpan(tIndex, -1);
4227 }
4228 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00004229 SkASSERT(angles.count() > 0);
4230 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
4231#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00004232 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00004233#endif
4234 return 0;
4235 }
caryclark@google.com47580692012-07-23 12:14:49 +00004236 test->buildAngles(tIndex, angles);
4237 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00004238 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00004239 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004240 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00004241 // hour after 6 o'clock
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004242 (void) Segment::SortAngles(angles, sorted);
caryclark@google.com47580692012-07-23 12:14:49 +00004243#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004244 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00004245#endif
4246 // walk the sorted angle fan to find the lowest angle
4247 // above the base point. Currently, the first angle in the sorted array
4248 // is 12 noon or an earlier hour (the next counterclockwise)
4249 int count = sorted.count();
4250 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004251 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00004252 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004253 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00004254 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00004255 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004256 if (angle->unsortable()) {
4257 continue;
4258 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004259 if (baseMatches && angle->isHorizontal()) {
4260 continue;
4261 }
4262 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00004263 test = angle->segment();
4264 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
4265 const SkPoint* pts = test->pts();
4266 indexDx = pts[2].fX - pts[1].fX - indexDx;
4267 }
caryclark@google.com47580692012-07-23 12:14:49 +00004268 if (indexDx < 0) {
4269 left = index;
4270 } else if (indexDx > 0) {
4271 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00004272 int previous = index - 1;
4273 if (previous < 0) {
4274 previous = count - 1;
4275 }
4276 const Angle* prev = sorted[previous];
4277 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
4278#if DEBUG_SORT
4279 SkDebugf("%s use prev\n", __FUNCTION__);
4280#endif
4281 right = previous;
4282 }
caryclark@google.com47580692012-07-23 12:14:49 +00004283 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004284 } else {
4285 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004286 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004287 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004288 if (left < 0 && right < 0) {
4289 left = mid;
4290 }
caryclark@google.com47580692012-07-23 12:14:49 +00004291 SkASSERT(left >= 0 || right >= 0);
4292 if (left < 0) {
4293 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004294 } else if (left >= 0 && mid >= 0 && right >= 0
4295 && sorted[mid]->sign() == sorted[right]->sign()) {
4296 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004297 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004298 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004299 test = angle->segment();
4300 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004301 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004302 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004303#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004304 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4305 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004306#endif
4307 } else {
4308 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004309 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004310 windValue = test->windValue(tIndex);
4311#if DEBUG_WINDING
4312 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4313 windValue);
4314#endif
4315 }
4316 // see if a + change in T results in a +/- change in X (compute x'(T))
4317 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004318 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4319 const SkPoint* pts = test->pts();
4320 dx = pts[2].fX - pts[1].fX - dx;
4321 }
caryclark@google.com47580692012-07-23 12:14:49 +00004322#if DEBUG_WINDING
4323 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4324#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004325 SkASSERT(dx != 0);
4326 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004327 winding += dx > 0 ? -windValue : windValue;
4328#if DEBUG_WINDING
4329 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4330#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004331 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004332 return winding;
4333}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004334
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004335#if SORTABLE_CONTOURS
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004336// OPTIMIZATION: not crazy about linear search here to find top active y.
4337// seems like we should break down and do the sort, or maybe sort each
rmistry@google.comd6176b02012-08-23 18:14:13 +00004338// contours' segments?
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004339// Once the segment array is built, there's no reason I can think of not to
4340// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004341// FIXME: return the contour found to pass to inner contour check
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004342static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004343 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004344 int cIndex = 0;
4345 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004346 SkScalar bestY = SK_ScalarMax;
4347 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004348 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004349 contour = contourList[cIndex];
4350 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004351 } while (!topStart && ++cIndex < contourCount);
4352 if (!topStart) {
4353 return NULL;
4354 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004355 while (++cIndex < contourCount) {
4356 contour = contourList[cIndex];
4357 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004358 continue;
4359 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004360 SkScalar testY = SK_ScalarMax;
4361 Segment* test = contour->topSegment(testY);
4362 if (!test || bestY <= testY) {
4363 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004364 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004365 topStart = test;
4366 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004367 }
4368 return topStart;
4369}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004370#endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004371
caryclark@google.com24bec792012-08-20 12:43:57 +00004372static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4373 int contourCount = contourList.count();
4374 Segment* result;
4375 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4376 Contour* contour = contourList[cIndex];
4377 result = contour->undoneSegment(start, end);
4378 if (result) {
4379 return result;
4380 }
4381 }
4382 return NULL;
4383}
4384
4385
4386
caryclark@google.come21cb182012-07-23 21:26:31 +00004387static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
4388 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004389 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004390 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004391 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4392 Segment* segment = backPtr.fOther;
4393 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004394 SkTDArray<Angle> angles;
4395 int done = 0;
4396 if (segment->activeAngle(tIndex, done, angles)) {
4397 Angle* last = angles.end() - 1;
4398 tIndex = last->start();
4399 endIndex = last->end();
4400 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004401 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004402 if (done == angles.count()) {
4403 chase.pop(&span);
4404 continue;
4405 }
4406 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004407 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004408#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004409 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004410#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004411 if (!sortable) {
4412 chase.pop(&span);
4413 continue;
4414 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004415 // find first angle, initialize winding to computed fWindSum
4416 int firstIndex = -1;
4417 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004418 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004419 do {
4420 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004421 segment = angle->segment();
4422 winding = segment->windSum(angle);
4423 } while (winding == SK_MinS32);
4424 int spanWinding = segment->spanSign(angle->start(), angle->end());
4425 #if DEBUG_WINDING
4426 SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
4427 __FUNCTION__, winding, spanWinding, contourWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00004428 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004429 // turn swinding into contourWinding
4430 if (spanWinding * winding < 0) {
4431 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004432 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004433 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004434 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004435 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004436 // we care about first sign and whether wind sum indicates this
4437 // edge is inside or outside. Maybe need to pass span winding
4438 // or first winding or something into this function?
4439 // advance to first undone angle, then return it and winding
4440 // (to set whether edges are active or not)
4441 int nextIndex = firstIndex + 1;
4442 int angleCount = sorted.count();
4443 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004444 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00004445 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00004446 do {
4447 SkASSERT(nextIndex != firstIndex);
4448 if (nextIndex == angleCount) {
4449 nextIndex = 0;
4450 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004451 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00004452 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004453 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00004454 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004455 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00004456 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
4457 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004458 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004459 tIndex = angle->start();
4460 endIndex = angle->end();
4461 int lesser = SkMin32(tIndex, endIndex);
4462 const Span& nextSpan = segment->span(lesser);
4463 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004464#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00004465 // FIXME: this be wrong. assign startWinding if edge is in
4466 // same direction. If the direction is opposite, winding to
4467 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00004468 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004469 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004470 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004471 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004472#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004473 break;
4474 }
4475 } while (++nextIndex != lastIndex);
4476 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004477 }
4478 return NULL;
4479}
4480
caryclark@google.com027de222012-07-12 12:52:50 +00004481#if DEBUG_ACTIVE_SPANS
4482static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004483 int index;
4484 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00004485 contourList[index]->debugShowActiveSpans();
4486 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004487 for (index = 0; index < contourList.count(); ++ index) {
4488 contourList[index]->validateActiveSpans();
4489 }
caryclark@google.com027de222012-07-12 12:52:50 +00004490}
4491#endif
4492
caryclark@google.com27c449a2012-07-27 18:26:38 +00004493static bool windingIsActive(int winding, int spanWinding) {
4494 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
4495 && (!winding || !spanWinding || winding == -spanWinding);
4496}
4497
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004498#if !SORTABLE_CONTOURS
4499static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
4500 int& endIndex) {
4501 Segment* result;
4502 do {
4503 int contourCount = contourList.count();
4504 int cIndex = 0;
4505 Segment* topStart;
4506 SkScalar bestY = SK_ScalarMax;
4507 Contour* contour;
4508 do {
4509 contour = contourList[cIndex];
4510 topStart = contour->topSortableSegment(bestY);
4511 } while (!topStart && ++cIndex < contourCount);
4512 if (!topStart) {
4513 return NULL;
4514 }
4515 while (++cIndex < contourCount) {
4516 contour = contourList[cIndex];
4517 if (bestY < contour->bounds().fTop) {
4518 continue;
4519 }
4520 SkScalar testY = SK_ScalarMax;
4521 Segment* test = contour->topSortableSegment(testY);
4522 if (!test || bestY <= testY) {
4523 continue;
4524 }
4525 topStart = test;
4526 bestY = testY;
4527 }
4528 result = topStart->findTop(index, endIndex);
4529 } while (!result);
4530 return result;
4531}
4532#endif
4533
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004534// Each segment may have an inside or an outside. Segments contained within
4535// winding may have insides on either side, and form a contour that should be
4536// ignored. Segments that are coincident with opposing direction segments may
4537// have outsides on either side, and should also disappear.
4538// 'Normal' segments will have one inside and one outside. Subsequent connections
4539// when winding should follow the intersection direction. If more than one edge
4540// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004541 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00004542 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004543// returns true if all edges were processed
4544static bool bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004545 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004546 bool unsortable = false;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004547 do {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004548#if SORTABLE_CONTOURS // old way
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004549 Segment* topStart = findTopContour(contourList);
caryclark@google.com15fa1382012-05-07 20:49:36 +00004550 if (!topStart) {
4551 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00004552 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004553 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00004554 // follow edges to intersection by changing the index by direction.
4555 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004556 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004557#else // new way: iterate while top is unsortable
4558 int index, endIndex;
4559 Segment* current = findSortableTop(contourList, index, endIndex);
4560 if (!current) {
4561 break;
4562 }
4563#endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004564 int contourWinding;
4565 if (firstContour) {
4566 contourWinding = 0;
4567 firstContour = false;
4568 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004569 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004570 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00004571 if (sumWinding == SK_MinS32) {
4572 sumWinding = current->computeSum(index, endIndex);
4573 }
4574 if (sumWinding == SK_MinS32) {
4575 contourWinding = innerContourCheck(contourList, current,
4576 index, endIndex);
4577 } else {
4578 contourWinding = sumWinding;
4579 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004580 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
4581 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00004582 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004583 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004584#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004585 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004586 sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00004587 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004588#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004589 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004590#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004591 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004592 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
4593#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004594 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004595 SkPoint lastPt;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004596 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004597 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004598 // FIXME: needs work. While it works in limited situations, it does
4599 // not always compute winding correctly. Active should be removed and instead
4600 // the initial winding should be correctly passed in so that if the
4601 // inner contour is wound the same way, it never finds an accumulated
4602 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00004603 // other than zero when resolving sorted angles.
caryclark@google.com27c449a2012-07-27 18:26:38 +00004604 bool active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004605 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004606 do {
caryclark@google.com0e08a192012-07-13 21:07:52 +00004607 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00004608 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
4609 __FUNCTION__, active ? "true" : "false",
4610 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004611 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004612 const SkPoint* firstPt = NULL;
4613 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004614 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004615 int nextStart = index;
4616 int nextEnd = endIndex;
4617 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004618 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004619 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00004620 if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
4621 lastPt = current->addCurveTo(index, endIndex, simple, true);
4622 SkASSERT(*firstPt == lastPt);
4623 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004624 break;
4625 }
4626 if (!firstPt) {
4627 firstPt = &current->addMoveTo(index, simple, active);
4628 }
4629 lastPt = current->addCurveTo(index, endIndex, simple, active);
4630 current = next;
4631 index = nextStart;
4632 endIndex = nextEnd;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004633 } while (*firstPt != lastPt && (active || !current->done()));
4634 if (firstPt && active) {
4635 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004636 SkDebugf("path.close();\n");
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004637 #endif
4638 simple.close();
4639 }
caryclark@google.come21cb182012-07-23 21:26:31 +00004640 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004641 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00004642 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004643 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004644 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00004645 break;
4646 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004647 int lesser = SkMin32(index, endIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004648 spanWinding = current->spanSign(index, endIndex);
4649 winding = current->windSum(lesser);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004650 bool inner = useInnerWinding(winding - spanWinding, winding);
4651 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004652 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
caryclark@google.com59823f72012-08-09 18:17:47 +00004653 " inner=%d result=%d\n",
caryclark@google.com2ddff932012-08-07 21:25:27 +00004654 __FUNCTION__, current->debugID(), current->t(lesser),
4655 spanWinding, winding, SkSign32(index - endIndex),
4656 useInnerWinding(winding - spanWinding, winding),
caryclark@google.com2ddff932012-08-07 21:25:27 +00004657 inner ? winding - spanWinding : winding);
4658 #endif
4659 if (inner) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004660 winding -= spanWinding;
4661 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004662 active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004663 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004664 } while (true);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004665 return !unsortable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004666}
4667
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004668// returns true if all edges were processed
4669static bool bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00004670 Segment* current;
4671 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004672 bool unsortable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00004673 while ((current = findUndone(contourList, start, end))) {
4674 const SkPoint* firstPt = NULL;
4675 SkPoint lastPt;
4676 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004677 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004678 int nextStart = start;
4679 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004680 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00004681 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00004682 if (firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
4683 lastPt = current->addCurveTo(start, end, simple, true);
4684 SkASSERT(*firstPt == lastPt);
4685 }
caryclark@google.com24bec792012-08-20 12:43:57 +00004686 break;
4687 }
4688 if (!firstPt) {
4689 firstPt = &current->addMoveTo(start, simple, true);
4690 }
4691 lastPt = current->addCurveTo(start, end, simple, true);
4692 current = next;
4693 start = nextStart;
4694 end = nextEnd;
4695 } while (*firstPt != lastPt);
4696 if (firstPt) {
4697 #if DEBUG_PATH_CONSTRUCTION
4698 SkDebugf("%s close\n", __FUNCTION__);
4699 #endif
4700 simple.close();
4701 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004702 #if DEBUG_ACTIVE_SPANS
4703 debugShowActiveSpans(contourList);
4704 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00004705 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004706 return !unsortable;
caryclark@google.com24bec792012-08-20 12:43:57 +00004707}
4708
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004709static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
4710 int contourCount = contourList.count();
4711 for (int cTest = 0; cTest < contourCount; ++cTest) {
4712 Contour* contour = contourList[cTest];
4713 contour->fixOtherTIndex();
4714 }
4715}
4716
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004717#if !SORTABLE_CONTOURS
4718static void sortSegments(SkTDArray<Contour*>& contourList) {
4719 int contourCount = contourList.count();
4720 for (int cTest = 0; cTest < contourCount; ++cTest) {
4721 Contour* contour = contourList[cTest];
4722 contour->sortSegments();
4723 }
4724}
4725#endif
4726
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004727static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004728 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004729 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004730 if (count == 0) {
4731 return;
4732 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004733 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004734 *list.append() = &contours[index];
4735 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004736 QSort<Contour>(list.begin(), list.end() - 1);
4737}
4738
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004739static void assemble(SkPath& simple) {
4740 // TODO: find the non-closed paths and connect them together
4741 SkASSERT(0);
4742}
4743
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004744void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004745 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004746 simple.reset();
4747 simple.setFillType(SkPath::kEvenOdd_FillType);
4748
4749 // turn path into list of segments
4750 SkTArray<Contour> contours;
4751 // FIXME: add self-intersecting cubics' T values to segment
4752 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004753 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004754 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004755 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004756 Contour** currentPtr = contourList.begin();
4757 if (!currentPtr) {
4758 return;
4759 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004760 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004761 // find all intersections between segments
4762 do {
4763 Contour** nextPtr = currentPtr;
4764 Contour* current = *currentPtr++;
4765 Contour* next;
4766 do {
4767 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004768 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004769 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004770 // eat through coincident edges
caryclark@google.com235f56a2012-09-14 14:19:30 +00004771 coincidenceCheck(contourList);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004772 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004773#if !SORTABLE_CONTOURS
4774 sortSegments(contourList);
4775#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004776 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004777 if (builder.xorMask() == kWinding_Mask
4778 ? !bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00004779 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004780 { // if some edges could not be resolved, assemble remaining fragments
4781 assemble(simple);
caryclark@google.com24bec792012-08-20 12:43:57 +00004782 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004783}
4784