blob: 1ea5b19de75c2b1918ff5423bc77562aa84a5e87 [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.com185c7c42012-10-19 18:26:24 +000032#define FORCE_RELEASE 1
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.com185c7c42012-10-19 18:26:24 +000045#define DEBUG_PATH_CONSTRUCTION 1
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.com185c7c42012-10-19 18:26:24 +0000556 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
557 || (rh.fVerb == SkPath::kLine_Verb && approximately_zero(rx) && approximately_zero(ry))) {
558 // See general unsortable comment below. This case can happen when
559 // one line has a non-zero change in t but no change in x and y.
560 fUnsortable = true;
561 rh.fUnsortable = true;
562 return this < &rh; // even with no solution, return a stable sort
563 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000564 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
565 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000566 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000567 // end of the shorter tangent to midway between the end points
568 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000569 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
570 double len = fTangent1.normalSquared();
571 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000572 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000573 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000574 int roots, rroots;
575 bool flip = false;
576 do {
577 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
578 double midX = (q[0].x + q[2].x) / 2;
579 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000580 ray[0] = q[1];
581 ray[1].x = midX;
582 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000583 SkASSERT(ray[0] != ray[1]);
584 roots = QuadRayIntersect(fPts, ray, i);
585 rroots = QuadRayIntersect(rh.fPts, ray, ri);
586 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000587 if (roots == 0 || rroots == 0) {
588 // FIXME: we don't have a solution in this case. The interim solution
589 // is to mark the edges as unsortable, exclude them from this and
590 // future computations, and allow the returned path to be fragmented
591 fUnsortable = true;
592 rh.fUnsortable = true;
593 return this < &rh; // even with no solution, return a stable sort
594 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000595 _Point loc;
596 double best = SK_ScalarInfinity;
597 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000598 int index;
599 for (index = 0; index < roots; ++index) {
600 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000601 dx = loc.x - ray[0].x;
602 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000603 dist = dx * dx + dy * dy;
604 if (best > dist) {
605 best = dist;
606 }
607 }
608 for (index = 0; index < rroots; ++index) {
609 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000610 dx = loc.x - ray[0].x;
611 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000612 dist = dx * dx + dy * dy;
613 if (best > dist) {
614 return fSide < 0;
615 }
616 }
617 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000618 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000619
caryclark@google.com47580692012-07-23 12:14:49 +0000620 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000621 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000622 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000623
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000624 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000625 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000626 }
627
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000628 int end() const {
629 return fEnd;
630 }
631
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000632 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000633 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000634 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000635
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000636 bool lengthen() {
637 int newEnd = fEnd;
638 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
639 fEnd = newEnd;
640 setSpans();
641 return true;
642 }
643 return false;
644 }
645
caryclark@google.coma461ff02012-10-11 12:54:23 +0000646 bool reverseLengthen() {
647 if (fReversed) {
648 return false;
649 }
650 int newEnd = fStart;
651 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
652 fEnd = newEnd;
653 fReversed = true;
654 setSpans();
655 return true;
656 }
657 return false;
658 }
659
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000660 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000661 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000662 fSegment = segment;
663 fStart = start;
664 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000665 fPts = orig;
666 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000667 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000668 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000669 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000670 setSpans();
671 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000672
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000673 void setSpans() {
674 double startT = (*fSpans)[fStart].fT;
675 double endT = (*fSpans)[fEnd].fT;
676 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000677 case SkPath::kLine_Verb:
678 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000679 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000680 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
681 fTangent1.lineEndPoints(l);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000682 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000683 break;
684 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000685 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000686 fTangent1.quadEndPoints(fQ, 0, 1);
687 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000688 break;
689 case SkPath::kCubic_Verb:
690 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000691 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000692 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000693 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000694 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000695 default:
696 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000697 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000698 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000699
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000700 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000701 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000702 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000703
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000704 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000705 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000706 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000707
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000708 const SkTDArray<Span>* spans() const {
709 return fSpans;
710 }
711
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000712 int start() const {
713 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000714 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000715
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000716 bool unsortable() const {
717 return fUnsortable;
718 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000719
caryclark@google.comc899ad92012-08-23 15:24:42 +0000720#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000721 const SkPoint* pts() const {
722 return fPts;
723 }
724
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000725 SkPath::Verb verb() const {
726 return fVerb;
727 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000728
caryclark@google.comc899ad92012-08-23 15:24:42 +0000729 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000730 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000731 }
732#endif
733
caryclark@google.com15fa1382012-05-07 20:49:36 +0000734private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000735 const SkPoint* fPts;
736 Quadratic fQ;
737 SkPath::Verb fVerb;
738 double fSide;
739 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000740 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000741 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000742 int fStart;
743 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000744 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000745 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000746};
747
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000748// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000749struct Bounds : public SkRect {
750 static bool Intersects(const Bounds& a, const Bounds& b) {
751 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
752 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
753 }
754
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000755 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
756 if (left < fLeft) {
757 fLeft = left;
758 }
759 if (top < fTop) {
760 fTop = top;
761 }
762 if (right > fRight) {
763 fRight = right;
764 }
765 if (bottom > fBottom) {
766 fBottom = bottom;
767 }
768 }
769
770 void add(const Bounds& toAdd) {
771 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
772 }
773
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000774 bool isEmpty() {
775 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000776 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000777 || isnan(fLeft) || isnan(fRight)
778 || isnan(fTop) || isnan(fBottom);
779 }
780
781 void setCubicBounds(const SkPoint a[4]) {
782 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000783 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000784 dRect.setBounds(cubic);
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 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000790 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000791 _Rect dRect;
792 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000793 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
794 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000795 }
796};
797
caryclark@google.com2ddff932012-08-07 21:25:27 +0000798static bool useInnerWinding(int outerWinding, int innerWinding) {
799 SkASSERT(outerWinding != innerWinding);
800 int absOut = abs(outerWinding);
801 int absIn = abs(innerWinding);
802 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
803 if (outerWinding * innerWinding < 0) {
804#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000805 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000806 outerWinding, innerWinding, result ? "true" : "false");
807#endif
808 }
809 return result;
810}
811
caryclark@google.com235f56a2012-09-14 14:19:30 +0000812static const bool opLookup[][2][2] = {
813 // ==0 !=0
814 // b a b a
815 {{true , false}, {false, true }}, // a - b
816 {{false, false}, {true , true }}, // a & b
817 {{true , true }, {false, false}}, // a | b
818 {{true , true }, {true , true }}, // a ^ b
819};
820
821static bool activeOp(bool angleIsOp, int otherNonZero, ShapeOp op) {
822 return opLookup[op][otherNonZero][angleIsOp];
823}
824
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000825class Segment {
826public:
827 Segment() {
828#if DEBUG_DUMP
829 fID = ++gSegmentID;
830#endif
831 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000832
833 bool operator<(const Segment& rh) const {
834 return fBounds.fTop < rh.fBounds.fTop;
835 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000836
caryclark@google.com9764cc62012-07-12 19:29:45 +0000837 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
838 if (activeAngleInner(index, done, angles)) {
839 return true;
840 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000841 double referenceT = fTs[index].fT;
842 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000843 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000844 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000845 return true;
846 }
847 }
848 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000849 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000850 return true;
851 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000852 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000853 return false;
854 }
855
caryclark@google.com9764cc62012-07-12 19:29:45 +0000856 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000857 Span* span = &fTs[index];
858 Segment* other = span->fOther;
859 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +0000860 return other->activeAngleInner(oIndex, done, angles);
861 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000862
caryclark@google.com9764cc62012-07-12 19:29:45 +0000863 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000864 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +0000865 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000866 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000867 if (upSpan.fWindValue) {
868 addAngle(angles, index, next);
869 if (upSpan.fDone) {
870 done++;
871 } else if (upSpan.fWindSum != SK_MinS32) {
872 return true;
873 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000874 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000875 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000876 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000877 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +0000878 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000879 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000880 if (downSpan.fWindValue) {
881 addAngle(angles, index, prev);
882 if (downSpan.fDone) {
883 done++;
884 } else if (downSpan.fWindSum != SK_MinS32) {
885 return true;
886 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000887 }
888 }
889 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000890 }
891
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000892 SkScalar activeTop() const {
893 SkASSERT(!done());
894 int count = fTs.count();
895 SkScalar result = SK_ScalarMax;
896 bool lastDone = true;
897 for (int index = 0; index < count; ++index) {
898 bool done = fTs[index].fDone;
899 if (!done || !lastDone) {
900 SkScalar y = yAtT(index);
901 if (result > y) {
902 result = y;
903 }
904 }
905 lastDone = done;
906 }
907 SkASSERT(result < SK_ScalarMax);
908 return result;
909 }
910
911 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000912 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000913 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000914#if DEBUG_ANGLE
915 if (angles.count() > 1) {
916 SkPoint angle0Pt, newPt;
917 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
918 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
919 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
920 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
921 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
922 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000923#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000924 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000925 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000926
caryclark@google.com2ddff932012-08-07 21:25:27 +0000927 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +0000928 double oEnd) {
929 int tIndex = -1;
930 int tCount = fTs.count();
931 int oIndex = -1;
932 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +0000933 do {
934 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000935 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +0000936 int tIndexStart = tIndex;
937 do {
938 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000939 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +0000940 int oIndexStart = oIndex;
941 double nextT;
942 do {
943 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000944 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000945 double oNextT;
946 do {
947 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000948 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000949 // at this point, spans before and after are at:
950 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
951 // if tIndexStart == 0, no prior span
952 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +0000953
caryclark@google.comcc905052012-07-25 20:59:42 +0000954 // advance the span with zero winding
955 // if the following span exists (not past the end, non-zero winding)
956 // connect the two edges
957 if (!fTs[tIndexStart].fWindValue) {
958 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
959 #if DEBUG_CONCIDENT
960 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
961 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000962 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
963 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +0000964 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000965 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000966 }
967 if (nextT < 1 && fTs[tIndex].fWindValue) {
968 #if DEBUG_CONCIDENT
969 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
970 __FUNCTION__, fID, other.fID, tIndex,
971 fTs[tIndex].fT, xyAtT(tIndex).fX,
972 xyAtT(tIndex).fY);
973 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000974 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000975 }
976 } else {
977 SkASSERT(!other.fTs[oIndexStart].fWindValue);
978 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
979 #if DEBUG_CONCIDENT
980 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
981 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000982 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
983 other.xyAtT(oIndexStart).fY);
984 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +0000985 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +0000986 }
987 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
988 #if DEBUG_CONCIDENT
989 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
990 __FUNCTION__, fID, other.fID, oIndex,
991 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
992 other.xyAtT(oIndex).fY);
993 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
994 #endif
995 }
996 }
997 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000998
caryclark@google.comcc905052012-07-25 20:59:42 +0000999 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1000 double oEnd) {
1001 // walk this to outsideTs[0]
1002 // walk other to outsideTs[1]
1003 // if either is > 0, add a pointer to the other, copying adjacent winding
1004 int tIndex = -1;
1005 int oIndex = -1;
1006 double tStart = outsideTs[0];
1007 double oStart = outsideTs[1];
1008 do {
1009 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001010 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001011 do {
1012 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001013 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001014 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001015 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001016 }
1017 tStart = fTs[tIndex].fT;
1018 oStart = other.fTs[oIndex].fT;
1019 do {
1020 double nextT;
1021 do {
1022 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001023 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001024 tStart = nextT;
1025 do {
1026 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001027 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001028 oStart = nextT;
1029 if (tStart == 1 && oStart == 1) {
1030 break;
1031 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001032 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001033 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001034 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001035
caryclark@google.com235f56a2012-09-14 14:19:30 +00001036 void addCubic(const SkPoint pts[4], bool operand) {
1037 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001038 fBounds.setCubicBounds(pts);
1039 }
1040
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001041 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001042 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001043 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001044 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001045 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001046 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001047 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001048 SkDebugf("path.%sTo(%1.9g,%1.9g",
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001049 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
1050 if (fVerb > 1) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001051 SkDebugf(", %1.9g,%1.9g", edge[2].fX, edge[2].fY);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001052 }
1053 if (fVerb > 2) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001054 SkDebugf(", %1.9g,%1.9g", edge[3].fX, edge[3].fY);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001055 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001056 SkDebugf(");\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001057 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001058 switch (fVerb) {
1059 case SkPath::kLine_Verb:
1060 path.lineTo(edge[1].fX, edge[1].fY);
1061 break;
1062 case SkPath::kQuad_Verb:
1063 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
1064 break;
1065 case SkPath::kCubic_Verb:
1066 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
1067 edge[3].fX, edge[3].fY);
1068 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001069 default:
1070 SkASSERT(0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001071 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001072 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001073 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001074 }
1075
caryclark@google.com235f56a2012-09-14 14:19:30 +00001076 void addLine(const SkPoint pts[2], bool operand) {
1077 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001078 fBounds.set(pts, 2);
1079 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001080
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001081 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
1082 const SkPoint& pt = xyAtT(tIndex);
1083 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001084 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001085 SkDebugf("path.moveTo(%1.9g, %1.9g);\n", pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001086 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001087 path.moveTo(pt.fX, pt.fY);
1088 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001089 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001090 }
1091
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001092 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001093 void addOtherT(int index, double otherT, int otherIndex) {
1094 Span& span = fTs[index];
caryclark@google.com185c7c42012-10-19 18:26:24 +00001095 if (precisely_less_than_zero(otherT)) {
1096 otherT = 0;
1097 } else if (precisely_greater_than_one(otherT)) {
1098 otherT = 1;
1099 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001100 span.fOtherT = otherT;
1101 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001102 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001103
caryclark@google.com235f56a2012-09-14 14:19:30 +00001104 void addQuad(const SkPoint pts[3], bool operand) {
1105 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001106 fBounds.setQuadBounds(pts);
1107 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001108
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001109 // Defer all coincident edge processing until
1110 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001111
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001112// no need to be tricky; insert in normal T order
1113// resolve overlapping ts when considering coincidence later
1114
1115 // add non-coincident intersection. Resulting edges are sorted in T.
1116 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001117 // FIXME: in the pathological case where there is a ton of intercepts,
1118 // binary search?
1119 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001120 size_t tCount = fTs.count();
caryclark@google.comc899ad92012-08-23 15:24:42 +00001121 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001122 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001123 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001124 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001125 newT = 1;
1126 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001127 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001128 // OPTIMIZATION: if there are three or more identical Ts, then
1129 // the fourth and following could be further insertion-sorted so
1130 // that all the edges are clockwise or counterclockwise.
1131 // This could later limit segment tests to the two adjacent
1132 // neighbors, although it doesn't help with determining which
1133 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001134 if (newT < fTs[index].fT) {
1135 insertedAt = index;
1136 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001137 }
1138 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001139 Span* span;
1140 if (insertedAt >= 0) {
1141 span = fTs.insert(insertedAt);
1142 } else {
1143 insertedAt = tCount;
1144 span = fTs.append();
1145 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001146 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001147 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001148 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001149 span->fWindSum = SK_MinS32;
1150 span->fWindValue = 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001151 span->fWindValueOpp = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001152 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001153 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001154 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001155 span->fUnsortableStart = false;
1156 span->fUnsortableEnd = false;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001157 return insertedAt;
1158 }
1159
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001160 // set spans from start to end to decrement by one
1161 // note this walks other backwards
1162 // FIMXE: there's probably an edge case that can be constructed where
1163 // two span in one segment are separated by float epsilon on one span but
1164 // not the other, if one segment is very small. For this
1165 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001166 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001167 // sorted? It feels better in such a case to match the span's other span
1168 // pointer since both coincident segments must contain the same spans.
1169 void addTCancel(double startT, double endT, Segment& other,
1170 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001171 SkASSERT(!approximately_negative(endT - startT));
1172 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001173 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001174 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001175 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001176 ++index;
1177 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001178 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001179 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001180 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001181 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001182 Span* test = &fTs[index];
1183 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001184 SkTDArray<double> outsideTs;
1185 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001186 do {
1187 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001188 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001189 double testT = test->fT;
1190 double oTestT = oTest->fT;
1191 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001192 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001193 if (decrement) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001194 if (binary) {
1195 --(span->fWindValueOpp);
1196 } else {
1197 decrementSpan(span);
1198 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001199 } else if (track && span->fT < 1 && oTestT < 1) {
1200 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001201 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001202 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001203 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001204 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001205 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1206 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1207 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001208 while (approximately_negative(otherTMatchStart - oSpan->fT)
1209 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001210 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001211 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001212 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001213 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001214 other.decrementSpan(oSpan);
1215 } else if (track && oSpan->fT < 1 && testT < 1) {
1216 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001217 }
1218 if (!oIndex) {
1219 break;
1220 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001221 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001222 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001223 test = span;
1224 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001225 } while (!approximately_negative(endT - test->fT));
1226 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001227 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001228 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001229 double tStart = outsideTs[0];
1230 double oStart = outsideTs[1];
1231 addCancelOutsides(tStart, oStart, other, oEndT);
1232 int count = outsideTs.count();
1233 if (count > 2) {
1234 double tStart = outsideTs[count - 2];
1235 double oStart = outsideTs[count - 1];
1236 addCancelOutsides(tStart, oStart, other, oEndT);
1237 }
caryclark@google.com18063442012-07-25 12:05:18 +00001238 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001239 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001240 double tStart = oOutsideTs[0];
1241 double oStart = oOutsideTs[1];
1242 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001243 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001244 }
1245
1246 // set spans from start to end to increment the greater by one and decrement
1247 // the lesser
caryclark@google.com235f56a2012-09-14 14:19:30 +00001248 void addTCoincident(const bool isXor, double startT, double endT,
1249 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001250 SkASSERT(!approximately_negative(endT - startT));
1251 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001252 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001253 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001254 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001255 ++index;
1256 }
1257 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001258 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001259 ++oIndex;
1260 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001261 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001262 Span* test = &fTs[index];
1263 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001264 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001265 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001266 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001267 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001268 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001269 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001270 bool decrementThis = (test->fWindValue < oTest->fWindValue) & !isXor;
1271 bool decrementOther = (test->fWindValue >= oTest->fWindValue) & !isXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001272 Span* end = test;
1273 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001274 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001275 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001276 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001277 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001278 if (transfer) {
1279 if (decrementOther) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001280 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001281 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001282 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001283 if (binary) {
1284 ++(end->fWindValueOpp);
1285 } else {
1286 ++(end->fWindValue);
1287 }
caryclark@google.com18063442012-07-25 12:05:18 +00001288 } else if (decrementSpan(end)) {
1289 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001290 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001291 } else if (oTest->fWindValue) {
1292 SkASSERT(!decrementOther);
1293 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1294 TrackOutside(xOutsideTs, end->fT, oStartT);
1295 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001296 }
1297 end = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001298 } while (approximately_negative(end->fT - test->fT));
caryclark@google.com59823f72012-08-09 18:17:47 +00001299 // because of the order in which coincidences are resolved, this and other
1300 // may not have the same intermediate points. Compute the corresponding
1301 // intermediate T values (using this as the master, other as the follower)
1302 // and walk other conditionally -- hoping that it catches up in the end
1303 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001304 Span* oEnd = oTest;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001305 while (!approximately_negative(oEndT - oEnd->fT) && approximately_negative(oEnd->fT - otherTMatch)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00001306 if (transfer) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001307 if (decrementThis) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001308 #ifdef SK_DEBUG
1309 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1310 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001311 if (binary) {
1312 ++(oEnd->fWindValueOpp);
1313 } else {
1314 ++(oEnd->fWindValue);
1315 }
caryclark@google.com18063442012-07-25 12:05:18 +00001316 } else if (other.decrementSpan(oEnd)) {
1317 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001318 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001319 } else if (test->fWindValue) {
1320 SkASSERT(!decrementOther);
1321 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1322 SkASSERT(0); // track for later?
1323 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001324 }
1325 oEnd = &other.fTs[++oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001326 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001327 test = end;
1328 oTest = oEnd;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001329 } while (!approximately_negative(endT - test->fT));
1330 SkASSERT(approximately_negative(oTest->fT - oEndT));
1331 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001332 if (!done()) {
1333 if (outsideTs.count()) {
1334 addCoinOutsides(outsideTs, other, oEndT);
1335 }
1336 if (xOutsideTs.count()) {
1337 addCoinOutsides(xOutsideTs, other, oEndT);
1338 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001339 }
1340 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001341 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001342 }
1343 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001344
caryclark@google.comcc905052012-07-25 20:59:42 +00001345 // FIXME: this doesn't prevent the same span from being added twice
1346 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001347 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001348 int tCount = fTs.count();
1349 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1350 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001351 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001352 break;
1353 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001354 if (approximately_negative(span.fT - t) && span.fOther == &other
1355 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001356#if DEBUG_ADD_T_PAIR
1357 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1358 __FUNCTION__, fID, t, other.fID, otherT);
1359#endif
1360 return;
1361 }
1362 }
caryclark@google.com47580692012-07-23 12:14:49 +00001363#if DEBUG_ADD_T_PAIR
1364 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1365 __FUNCTION__, fID, t, other.fID, otherT);
1366#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001367 int insertedAt = addT(t, &other);
1368 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001369 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001370 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001371 matchWindingValue(insertedAt, t, borrowWind);
1372 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001373 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001374
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001375 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001376 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001377 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1378 addAngle(angles, end, start);
1379 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001380 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001381 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001382 int tIndex = nextExactSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001383 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001384 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001385 }
1386 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001387
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001388 const Bounds& bounds() const {
1389 return fBounds;
1390 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001391
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001392 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001393 double referenceT = fTs[index].fT;
1394 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001395 #if PRECISE_T_SORT
1396 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
1397 buildAnglesInner(lesser, angles);
1398 }
1399 do {
1400 buildAnglesInner(index, angles);
1401 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
1402 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001403 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001404 buildAnglesInner(lesser, angles);
1405 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001406 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001407 buildAnglesInner(index, angles);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001408 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00001409 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001410 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001411
1412 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1413 Span* span = &fTs[index];
1414 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001415 // if there is only one live crossing, and no coincidence, continue
1416 // in the same direction
1417 // if there is coincidence, the only choice may be to reverse direction
1418 // find edge on either side of intersection
1419 int oIndex = span->fOtherIndex;
1420 // if done == -1, prior span has already been processed
1421 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001422 #if PRECISE_T_SORT
1423 int next = other->nextExactSpan(oIndex, step);
1424 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001425 int next = other->nextSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001426 #endif
1427 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001428 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001429 #if PRECISE_T_SORT
1430 next = other->nextExactSpan(oIndex, step);
1431 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001432 next = other->nextSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001433 #endif
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001434 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001435 // add candidate into and away from junction
1436 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001437 }
1438
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001439 // figure out if the segment's ascending T goes clockwise or not
1440 // not enough context to write this as shown
1441 // instead, add all segments meeting at the top
1442 // sort them using buildAngleList
1443 // find the first in the sort
1444 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001445 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001446 SkASSERT(0); // incomplete
1447 return false;
1448 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001449
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001450 // FIXME may not need this at all
1451 // FIXME once I figure out the logic, merge this and too close to call
1452 // NOTE not sure if tiny triangles can ever form at the edge, so until we
1453 // see one, only worry about triangles that happen away from 0 and 1
1454 void collapseTriangles(bool isXor) {
1455 if (fTs.count() < 3) { // require t=0, x, 1 at minimum
1456 return;
1457 }
1458 int lastIndex = 1;
1459 double lastT;
1460 while (approximately_less_than_zero((lastT = fTs[lastIndex].fT))) {
1461 ++lastIndex;
1462 }
1463 if (approximately_greater_than_one(lastT)) {
1464 return;
1465 }
1466 int matchIndex = lastIndex;
1467 do {
1468 Span& match = fTs[++matchIndex];
1469 double matchT = match.fT;
1470 if (approximately_greater_than_one(matchT)) {
1471 return;
1472 }
1473 if (matchT == lastT) {
1474 goto nextSpan;
1475 }
1476 if (approximately_negative(matchT - lastT)) {
1477 Span& last = fTs[lastIndex];
1478 Segment* lOther = last.fOther;
1479 double lT = last.fOtherT;
1480 if (approximately_less_than_zero(lT) || approximately_greater_than_one(lT)) {
1481 goto nextSpan;
1482 }
1483 Segment* mOther = match.fOther;
1484 double mT = match.fOtherT;
1485 if (approximately_less_than_zero(mT) || approximately_greater_than_one(mT)) {
1486 goto nextSpan;
1487 }
1488 // add new point to connect adjacent spurs
1489 int count = lOther->fTs.count();
1490 for (int index = 0; index < count; ++index) {
1491 Span& span = lOther->fTs[index];
1492 if (span.fOther == mOther && approximately_zero(span.fOtherT - mT)) {
1493 goto nextSpan;
1494 }
1495 }
1496 mOther->addTPair(mT, *lOther, lT, false);
1497 // FIXME ? this could go on to detect that spans on mOther, lOther are now coincident
1498 }
1499 nextSpan:
1500 lastIndex = matchIndex;
1501 lastT = matchT;
1502 } while (true);
1503 }
1504
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001505 int computeSum(int startIndex, int endIndex) {
1506 SkTDArray<Angle> angles;
1507 addTwoAngles(startIndex, endIndex, angles);
1508 buildAngles(endIndex, angles);
caryclark@google.comd1688742012-09-18 20:08:37 +00001509 // OPTIMIZATION: check all angles to see if any have computed wind sum
1510 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001511 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001512 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001513#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00001514 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001515#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001516 if (!sortable) {
1517 return SK_MinS32;
1518 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001519 int angleCount = angles.count();
1520 const Angle* angle;
1521 const Segment* base;
1522 int winding;
1523 int firstIndex = 0;
1524 do {
1525 angle = sorted[firstIndex];
1526 base = angle->segment();
1527 winding = base->windSum(angle);
1528 if (winding != SK_MinS32) {
1529 break;
1530 }
1531 if (++firstIndex == angleCount) {
1532 return SK_MinS32;
1533 }
1534 } while (true);
1535 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001536 int spanWinding = base->spanSign(angle);
1537 bool inner = useInnerWinding(winding + spanWinding, winding);
1538 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001539 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001540 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001541 inner ? winding + spanWinding : winding);
1542 #endif
1543 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001544 winding += spanWinding;
1545 }
1546 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001547 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001548 #endif
1549 int nextIndex = firstIndex + 1;
1550 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001551 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001552 do {
1553 if (nextIndex == angleCount) {
1554 nextIndex = 0;
1555 }
1556 angle = sorted[nextIndex];
1557 Segment* segment = angle->segment();
1558 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001559 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001560 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001561 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001562 maxWinding = winding;
1563 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001564 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001565 }
1566 } while (++nextIndex != lastIndex);
1567 return windSum(SkMin32(startIndex, endIndex));
1568 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001569
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001570 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001571 int bestT = -1;
1572 SkScalar top = bounds().fTop;
1573 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001574 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001575 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001576 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001577 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001578 if (fTs[start].fWindValue == 0) {
1579 continue;
1580 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001581 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001582 double startT = fTs[start].fT;
1583 double endT = fTs[end].fT;
1584 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001585 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001586 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001587 // FIXME: always use original and limit results to T values within
1588 // start t and end t.
1589 // OPTIMIZE: use specialty function that intersects ray with curve,
1590 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001591 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1592 false, intersections);
1593 if (pts == 0) {
1594 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001595 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001596 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1597 // if the intersection is edge on, wait for another one
1598 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001599 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001600 for (int index = 0; index < pts; ++index) {
1601 SkPoint pt;
1602 double foundT = intersections.fT[0][index];
1603 double testT = startT + (endT - startT) * foundT;
1604 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1605 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001606 if (fVerb > SkPath::kLine_Verb
1607 && !approximately_less_than_zero(foundT)
1608 && !approximately_greater_than_one(foundT)) {
1609 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1610 if (approximately_zero(dx)) {
1611 continue;
1612 }
1613 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001614 bestY = pt.fY;
1615 bestT = foundT < 1 ? start : end;
1616 hitT = testT;
1617 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001618 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001619 } while (fTs[end].fT != 1);
1620 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001621 }
caryclark@google.com18063442012-07-25 12:05:18 +00001622
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001623 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1624 // if a segment is connected to this one, consider it crossing
1625 int tIndex;
1626 if (fPts[0].fX == basePt.fX) {
1627 tIndex = 0;
1628 do {
1629 const Span& sSpan = fTs[tIndex];
1630 const Segment* sOther = sSpan.fOther;
1631 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1632 continue;
1633 }
1634 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1635 : sOther->fBounds.fRight > basePt.fX) {
1636 return true;
1637 }
1638 } while (fTs[++tIndex].fT == 0);
1639 }
1640 if (fPts[fVerb].fX == basePt.fX) {
1641 tIndex = fTs.count() - 1;
1642 do {
1643 const Span& eSpan = fTs[tIndex];
1644 const Segment* eOther = eSpan.fOther;
1645 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1646 continue;
1647 }
1648 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1649 : eOther->fBounds.fRight > basePt.fX) {
1650 return true;
1651 }
1652 } while (fTs[--tIndex].fT == 1);
1653 }
1654 return false;
1655 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001656
caryclark@google.com18063442012-07-25 12:05:18 +00001657 bool decrementSpan(Span* span) {
1658 SkASSERT(span->fWindValue > 0);
1659 if (--(span->fWindValue) == 0) {
1660 span->fDone = true;
1661 ++fDoneSpans;
1662 return true;
1663 }
1664 return false;
1665 }
1666
caryclark@google.com15fa1382012-05-07 20:49:36 +00001667 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001668 SkASSERT(fDoneSpans <= fTs.count());
1669 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001670 }
1671
caryclark@google.com47580692012-07-23 12:14:49 +00001672 bool done(const Angle& angle) const {
1673 int start = angle.start();
1674 int end = angle.end();
1675 const Span& mSpan = fTs[SkMin32(start, end)];
1676 return mSpan.fDone;
1677 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001678
caryclark@google.com235f56a2012-09-14 14:19:30 +00001679 Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00001680 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001681 bool& unsortable, ShapeOp op,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001682 const int aXorMask, const int bXorMask) {
1683 const int startIndex = nextStart;
1684 const int endIndex = nextEnd;
1685 int outerWinding = winding;
1686 int innerWinding = winding + spanWinding;
1687 #if DEBUG_WINDING
1688 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1689 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
1690 #endif
1691 if (useInnerWinding(outerWinding, innerWinding)) {
1692 outerWinding = innerWinding;
1693 }
1694 SkASSERT(startIndex != endIndex);
1695 int count = fTs.count();
1696 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1697 : startIndex > 0);
1698 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001699 #if PRECISE_T_SORT
1700 int end = nextExactSpan(startIndex, step);
1701 #else
caryclark@google.com235f56a2012-09-14 14:19:30 +00001702 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001703 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001704 SkASSERT(end >= 0);
1705 Span* endSpan = &fTs[end];
1706 Segment* other;
1707 if (isSimple(end)) {
1708 // mark the smaller of startIndex, endIndex done, and all adjacent
1709 // spans with the same T value (but not 'other' spans)
1710 #if DEBUG_WINDING
1711 SkDebugf("%s simple\n", __FUNCTION__);
1712 #endif
1713 markDone(SkMin32(startIndex, endIndex), outerWinding);
1714 other = endSpan->fOther;
1715 nextStart = endSpan->fOtherIndex;
1716 double startT = other->fTs[nextStart].fT;
1717 nextEnd = nextStart;
1718 do {
1719 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001720 }
1721 #if PRECISE_T_SORT
1722 while (precisely_zero(startT - other->fTs[nextEnd].fT));
1723 #else
1724 while (approximately_zero(startT - other->fTs[nextEnd].fT));
1725 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001726 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1727 return other;
1728 }
1729 // more than one viable candidate -- measure angles to find best
1730 SkTDArray<Angle> angles;
1731 SkASSERT(startIndex - endIndex != 0);
1732 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1733 addTwoAngles(startIndex, end, angles);
1734 buildAngles(end, angles);
1735 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001736 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001737 int angleCount = angles.count();
1738 int firstIndex = findStartingEdge(sorted, startIndex, end);
1739 SkASSERT(firstIndex >= 0);
1740 #if DEBUG_SORT
1741 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
1742 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001743 if (!sortable) {
1744 unsortable = true;
1745 return NULL;
1746 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001747 SkASSERT(sorted[firstIndex]->segment() == this);
1748 #if DEBUG_WINDING
1749 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
1750 #endif
1751 int aSumWinding = winding;
1752 int bSumWinding = winding;
1753 bool angleIsOp = sorted[firstIndex]->segment()->operand();
1754 int angleSpan = spanSign(sorted[firstIndex]);
1755 if (angleIsOp) {
1756 bSumWinding -= angleSpan;
1757 } else {
1758 aSumWinding -= angleSpan;
1759 }
1760 int nextIndex = firstIndex + 1;
1761 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1762 const Angle* foundAngle = NULL;
1763 // FIXME: found done logic probably fails if there are more than 4
1764 // sorted angles. It should bias towards the first and last undone
1765 // edges -- but not sure that it won't choose a middle (incorrect)
1766 // edge if one is undone
1767 bool foundDone = false;
1768 bool foundDone2 = false;
1769 // iterate through the angle, and compute everyone's winding
1770 bool altFlipped = false;
1771 bool foundFlipped = false;
1772 int foundMax = SK_MinS32;
1773 int foundSum = SK_MinS32;
1774 Segment* nextSegment;
1775 int lastNonZeroSum = winding;
1776 do {
1777 if (nextIndex == angleCount) {
1778 nextIndex = 0;
1779 }
1780 const Angle* nextAngle = sorted[nextIndex];
1781 nextSegment = nextAngle->segment();
1782 angleIsOp = nextSegment->operand();
1783 int sumWinding = angleIsOp ? bSumWinding : aSumWinding;
1784 int maxWinding = sumWinding;
1785 if (sumWinding) {
1786 lastNonZeroSum = sumWinding;
1787 }
1788 sumWinding -= nextSegment->spanSign(nextAngle);
1789 int xorMask = nextSegment->operand() ? bXorMask : aXorMask;
1790 bool otherNonZero;
1791 if (angleIsOp) {
1792 bSumWinding = sumWinding;
1793 otherNonZero = aSumWinding & aXorMask;
1794 } else {
1795 aSumWinding = sumWinding;
1796 otherNonZero = bSumWinding & bXorMask;
1797 }
1798 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00001799 #if 0 && DEBUG_WINDING
caryclark@google.com235f56a2012-09-14 14:19:30 +00001800 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
1801 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
1802 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
1803 #endif
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00001804
caryclark@google.com235f56a2012-09-14 14:19:30 +00001805 if (!(sumWinding & xorMask) && activeOp(angleIsOp, otherNonZero, op)) {
1806 if (!active) {
1807 markDone(SkMin32(startIndex, endIndex), outerWinding);
1808 // FIXME: seems like a bug that this isn't calling userInnerWinding
1809 nextSegment->markWinding(SkMin32(nextAngle->start(),
1810 nextAngle->end()), maxWinding);
1811 #if DEBUG_WINDING
1812 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
1813 #endif
1814 return NULL;
1815 }
1816 if (!foundAngle || foundDone) {
1817 foundAngle = nextAngle;
1818 foundDone = nextSegment->done(*nextAngle);
1819 foundFlipped = altFlipped;
1820 foundMax = maxWinding;
1821 }
1822 continue;
1823 }
1824 if (!(maxWinding & xorMask) && (!foundAngle || foundDone2)
1825 && activeOp(angleIsOp, otherNonZero, op)) {
1826 #if DEBUG_WINDING
1827 if (foundAngle && foundDone2) {
1828 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
1829 }
1830 #endif
1831 foundAngle = nextAngle;
1832 foundDone2 = nextSegment->done(*nextAngle);
1833 foundFlipped = altFlipped;
1834 foundSum = sumWinding;
1835 }
1836 if (nextSegment->done()) {
1837 continue;
1838 }
1839 // if the winding is non-zero, nextAngle does not connect to
1840 // current chain. If we haven't done so already, mark the angle
1841 // as done, record the winding value, and mark connected unambiguous
1842 // segments as well.
1843 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
1844 if (useInnerWinding(maxWinding, sumWinding)) {
1845 maxWinding = sumWinding;
1846 }
1847 Span* last;
1848 if (foundAngle) {
1849 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
1850 } else {
1851 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
1852 }
1853 if (last) {
1854 *chase.append() = last;
1855 }
1856 }
1857 } while (++nextIndex != lastIndex);
1858 markDone(SkMin32(startIndex, endIndex), outerWinding);
1859 if (!foundAngle) {
1860 return NULL;
1861 }
1862 nextStart = foundAngle->start();
1863 nextEnd = foundAngle->end();
1864 nextSegment = foundAngle->segment();
1865 int flipped = foundFlipped ? -1 : 1;
1866 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
1867 SkMin32(nextStart, nextEnd));
1868 if (winding) {
1869 #if DEBUG_WINDING
1870 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
1871 if (foundSum == SK_MinS32) {
1872 SkDebugf("?");
1873 } else {
1874 SkDebugf("%d", foundSum);
1875 }
1876 SkDebugf(" foundMax=");
1877 if (foundMax == SK_MinS32) {
1878 SkDebugf("?");
1879 } else {
1880 SkDebugf("%d", foundMax);
1881 }
1882 SkDebugf("\n");
1883 #endif
1884 winding = foundSum;
1885 }
1886 #if DEBUG_WINDING
1887 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
1888 #endif
1889 return nextSegment;
1890 }
caryclark@google.com47580692012-07-23 12:14:49 +00001891
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001892 // so the span needs to contain the pairing info found here
1893 // this should include the winding computed for the edge, and
1894 // what edge it connects to, and whether it is discarded
1895 // (maybe discarded == abs(winding) > 1) ?
1896 // only need derivatives for duration of sorting, add a new struct
1897 // for pairings, remove extra spans that have zero length and
1898 // reference an unused other
1899 // for coincident, the last span on the other may be marked done
1900 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001901
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001902 // if loop is exhausted, contour may be closed.
1903 // FIXME: pass in close point so we can check for closure
1904
1905 // given a segment, and a sense of where 'inside' is, return the next
1906 // segment. If this segment has an intersection, or ends in multiple
1907 // segments, find the mate that continues the outside.
1908 // note that if there are multiples, but no coincidence, we can limit
1909 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00001910
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001911 // mark found segments as done
1912
caryclark@google.com15fa1382012-05-07 20:49:36 +00001913 // start is the index of the beginning T of this edge
1914 // it is guaranteed to have an end which describes a non-zero length (?)
1915 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00001916 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001917 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
1918 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001919 const int startIndex = nextStart;
1920 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001921 int outerWinding = winding;
1922 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00001923 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001924 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1925 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00001926 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001927 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001928 outerWinding = innerWinding;
1929 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001930 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001931 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001932 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1933 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001934 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001935 #if PRECISE_T_SORT
1936 int end = nextExactSpan(startIndex, step);
1937 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001938 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001939 #endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001940 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001941 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001942 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001943 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001944 // mark the smaller of startIndex, endIndex done, and all adjacent
1945 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001946 #if DEBUG_WINDING
1947 SkDebugf("%s simple\n", __FUNCTION__);
1948 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001949 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001950 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001951 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00001952 double startT = other->fTs[nextStart].fT;
1953 nextEnd = nextStart;
1954 do {
1955 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001956 }
1957 #if PRECISE_T_SORT
1958 while (precisely_zero(startT - other->fTs[nextEnd].fT));
1959 #else
1960 while (approximately_zero(startT - other->fTs[nextEnd].fT));
1961 #endif
caryclark@google.com495f8e42012-05-31 13:13:11 +00001962 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001963 return other;
1964 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001965 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001966 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001967 SkASSERT(startIndex - endIndex != 0);
1968 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001969 addTwoAngles(startIndex, end, angles);
1970 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001971 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001972 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001973 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001974 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001975 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00001976 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001977 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001978 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001979 if (!sortable) {
1980 unsortable = true;
1981 return NULL;
1982 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001983 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001984 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001985 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00001986 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001987 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001988 int nextIndex = firstIndex + 1;
1989 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1990 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00001991 // FIXME: found done logic probably fails if there are more than 4
1992 // sorted angles. It should bias towards the first and last undone
1993 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001994 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00001995 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00001996 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001997 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00001998 bool altFlipped = false;
1999 bool foundFlipped = false;
2000 int foundMax = SK_MinS32;
2001 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002002 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002003 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002004 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002005 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002006 nextIndex = 0;
2007 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002008 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002009 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002010 if (sumWinding) {
2011 lastNonZeroSum = sumWinding;
2012 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002013 nextSegment = nextAngle->segment();
caryclark@google.com2ddff932012-08-07 21:25:27 +00002014 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002015 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002016 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002017 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002018 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2019 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002020 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002021 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002022 if (!active) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002023 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002024 // FIXME: seems like a bug that this isn't calling userInnerWinding
caryclark@google.com47580692012-07-23 12:14:49 +00002025 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002026 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002027 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002028 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002029 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002030 return NULL;
2031 }
caryclark@google.com47580692012-07-23 12:14:49 +00002032 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002033 foundAngle = nextAngle;
caryclark@google.com47580692012-07-23 12:14:49 +00002034 foundDone = nextSegment->done(*nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002035 foundFlipped = altFlipped;
2036 foundMax = maxWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002037 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002038 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002039 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002040 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002041 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002042 if (foundAngle && foundDone2) {
2043 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002044 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002045 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002046 foundAngle = nextAngle;
caryclark@google.com24bec792012-08-20 12:43:57 +00002047 foundDone2 = nextSegment->done(*nextAngle);
2048 foundFlipped = altFlipped;
2049 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002050 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002051 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002052 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002053 }
2054 // if the winding is non-zero, nextAngle does not connect to
2055 // current chain. If we haven't done so already, mark the angle
2056 // as done, record the winding value, and mark connected unambiguous
2057 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002058 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002059 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002060 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002061 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002062 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002063 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002064 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002065 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002066 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002067 }
2068 if (last) {
2069 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002070 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002071 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002072 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002073 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002074 if (!foundAngle) {
2075 return NULL;
2076 }
2077 nextStart = foundAngle->start();
2078 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002079 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002080 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002081 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2082 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002083 if (winding) {
2084 #if DEBUG_WINDING
2085 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2086 if (foundSum == SK_MinS32) {
2087 SkDebugf("?");
2088 } else {
2089 SkDebugf("%d", foundSum);
2090 }
2091 SkDebugf(" foundMax=");
2092 if (foundMax == SK_MinS32) {
2093 SkDebugf("?");
2094 } else {
2095 SkDebugf("%d", foundMax);
2096 }
2097 SkDebugf("\n");
2098 #endif
2099 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002100 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002101 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002102 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002103 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002104 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002105 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002106
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002107 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002108 const int startIndex = nextStart;
2109 const int endIndex = nextEnd;
2110 SkASSERT(startIndex != endIndex);
2111 int count = fTs.count();
2112 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2113 : startIndex > 0);
2114 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002115 #if PRECISE_T_SORT
2116 int end = nextExactSpan(startIndex, step);
2117 #else
caryclark@google.com24bec792012-08-20 12:43:57 +00002118 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002119 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002120 SkASSERT(end >= 0);
2121 Span* endSpan = &fTs[end];
2122 Segment* other;
2123 markDone(SkMin32(startIndex, endIndex), 1);
2124 if (isSimple(end)) {
2125 #if DEBUG_WINDING
2126 SkDebugf("%s simple\n", __FUNCTION__);
2127 #endif
2128 other = endSpan->fOther;
2129 nextStart = endSpan->fOtherIndex;
2130 double startT = other->fTs[nextStart].fT;
2131 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002132 if ((approximately_less_than_zero(startT) && step < 0)
2133 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002134 step = -step;
2135 SkDEBUGCODE(firstLoop = false;)
2136 }
2137 do {
2138 nextEnd = nextStart;
2139 do {
2140 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002141 }
2142 #if PRECISE_T_SORT
2143 while (precisely_zero(startT - other->fTs[nextEnd].fT));
2144 #else
2145 while (approximately_zero(startT - other->fTs[nextEnd].fT));
2146 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002147 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2148 break;
2149 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002150 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002151 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002152 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002153 SkDEBUGCODE(firstLoop = false;)
2154 step = -step;
2155 } while (true);
2156 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2157 return other;
2158 }
2159 SkTDArray<Angle> angles;
2160 SkASSERT(startIndex - endIndex != 0);
2161 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2162 addTwoAngles(startIndex, end, angles);
2163 buildAngles(end, angles);
2164 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002165 bool sortable = SortAngles(angles, sorted);
caryclark@google.com24bec792012-08-20 12:43:57 +00002166 int angleCount = angles.count();
2167 int firstIndex = findStartingEdge(sorted, startIndex, end);
2168 SkASSERT(firstIndex >= 0);
2169 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002170 debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002171 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002172 if (!sortable) {
2173 unsortable = true;
2174 return NULL;
2175 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002176 SkASSERT(sorted[firstIndex]->segment() == this);
2177 int nextIndex = firstIndex + 1;
2178 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2179 const Angle* nextAngle;
2180 Segment* nextSegment;
2181 do {
2182 if (nextIndex == angleCount) {
2183 nextIndex = 0;
2184 }
2185 nextAngle = sorted[nextIndex];
2186 nextSegment = nextAngle->segment();
2187 if (!nextSegment->done(*nextAngle)) {
2188 break;
2189 }
2190 if (++nextIndex == lastIndex) {
2191 return NULL;
2192 }
2193 } while (true);
2194 nextStart = nextAngle->start();
2195 nextEnd = nextAngle->end();
2196 return nextSegment;
2197 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002198
2199 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2200 int angleCount = sorted.count();
2201 int firstIndex = -1;
2202 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2203 const Angle* angle = sorted[angleIndex];
2204 if (angle->segment() == this && angle->start() == end &&
2205 angle->end() == start) {
2206 firstIndex = angleIndex;
2207 break;
2208 }
2209 }
2210 return firstIndex;
2211 }
2212
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002213 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com235f56a2012-09-14 14:19:30 +00002214 void findTooCloseToCall(bool isXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002215 int count = fTs.count();
2216 if (count < 3) { // require t=0, x, 1 at minimum
2217 return;
2218 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002219 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002220 int moCount;
2221 Span* match;
2222 Segment* mOther;
2223 do {
2224 match = &fTs[matchIndex];
2225 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002226 // FIXME: allow quads, cubics to be near coincident?
2227 if (mOther->fVerb == SkPath::kLine_Verb) {
2228 moCount = mOther->fTs.count();
2229 if (moCount >= 3) {
2230 break;
2231 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002232 }
2233 if (++matchIndex >= count) {
2234 return;
2235 }
2236 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002237 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002238 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002239 // look for a pair of nearby T values that map to the same (x,y) value
2240 // if found, see if the pair of other segments share a common point. If
2241 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002242 for (int index = matchIndex + 1; index < count; ++index) {
2243 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002244 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002245 continue;
2246 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002247 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002248 if (tOther->fVerb != SkPath::kLine_Verb) {
2249 continue; // FIXME: allow quads, cubics to be near coincident?
2250 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002251 int toCount = tOther->fTs.count();
2252 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002253 continue;
2254 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002255 const SkPoint* testPt = &xyAtT(test);
2256 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002257 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002258 moCount = toCount;
2259 match = test;
2260 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002261 matchPt = testPt;
2262 continue;
2263 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002264 int moStart = -1;
2265 int moEnd = -1;
2266 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002267 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002268 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002269 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002270 continue;
2271 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002272 if (moSpan.fOther == this) {
2273 if (moSpan.fOtherT == match->fT) {
2274 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002275 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002276 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002277 continue;
2278 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002279 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002280 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2281 moStart = -1;
2282 break;
2283 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002284 SkASSERT(moEnd == -1);
2285 moEnd = moIndex;
2286 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002287 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002288 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002289 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002290 continue;
2291 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002292 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002293 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002294 continue;
2295 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002296 int toStart = -1;
2297 int toEnd = -1;
2298 double toStartT, toEndT;
2299 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2300 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002301 if (toSpan.fDone) {
2302 continue;
2303 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002304 if (toSpan.fOther == this) {
2305 if (toSpan.fOtherT == test->fT) {
2306 toStart = toIndex;
2307 toStartT = toSpan.fT;
2308 }
2309 continue;
2310 }
2311 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002312 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2313 moStart = -1;
2314 break;
2315 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002316 SkASSERT(toEnd == -1);
2317 toEnd = toIndex;
2318 toEndT = toSpan.fT;
2319 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002320 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002321 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2322 if (toStart <= 0 || toEnd <= 0) {
2323 continue;
2324 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002325 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002326 continue;
2327 }
2328 // test to see if the segment between there and here is linear
2329 if (!mOther->isLinear(moStart, moEnd)
2330 || !tOther->isLinear(toStart, toEnd)) {
2331 continue;
2332 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002333 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002334 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002335 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002336 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002337 // FIXME: this is bogus for multiple ops
2338 // the xorMask needs to be accumulated from the union of the two
2339 // edges -- which means that the segment must have its own copy of the mask
2340 mOther->addTCoincident(isXor, moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002341 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002342 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002343 }
2344
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002345 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002346 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002347 // a) mark spans with either end unsortable as done, or
2348 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2349 // when encountering an unsortable span
2350
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002351 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2352 // and use more concise logic like the old edge walker code?
2353 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002354 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002355 // iterate through T intersections and return topmost
2356 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002357 SkASSERT(!done());
2358 int firstT;
2359 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002360 SkPoint topPt;
2361 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002362 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002363 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002364 bool lastDone = true;
2365 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002366 const Span& span = fTs[index];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002367 if (!span.fDone || !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002368 const SkPoint& intercept = xyAtT(&span);
2369 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2370 && topPt.fX > intercept.fX)) {
2371 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002372 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002373 } else if (topPt == intercept) {
2374 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002375 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002376 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002377 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002378 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002379 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002380 int step = 1;
2381 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002382 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002383 step = -1;
2384 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002385 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002386 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002387 // if the topmost T is not on end, or is three-way or more, find left
2388 // look for left-ness from tLeft to firstT (matching y of other)
2389 SkTDArray<Angle> angles;
2390 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002391 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002392 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002393 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002394 (void) SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002395 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00002396 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002397 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002398 // skip edges that have already been processed
2399 firstT = -1;
2400 Segment* leftSegment;
2401 do {
2402 const Angle* angle = sorted[++firstT];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002403 if (angle->unsortable()) {
2404 // FIXME: if all angles are unsortable, find next topmost
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002405 if (firstT >= angles.count() - 1) {
2406 #if SORTABLE_CONTOURS
2407 SkASSERT(0);
2408 #endif
2409 return NULL;
2410 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002411 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002412 leftSegment = angle->segment();
2413 tIndex = angle->end();
2414 endIndex = angle->start();
2415 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002416 return leftSegment;
2417 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002418
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002419 // FIXME: not crazy about this
2420 // when the intersections are performed, the other index is into an
2421 // incomplete array. as the array grows, the indices become incorrect
2422 // while the following fixes the indices up again, it isn't smart about
2423 // skipping segments whose indices are already correct
2424 // assuming we leave the code that wrote the index in the first place
2425 void fixOtherTIndex() {
2426 int iCount = fTs.count();
2427 for (int i = 0; i < iCount; ++i) {
2428 Span& iSpan = fTs[i];
2429 double oT = iSpan.fOtherT;
2430 Segment* other = iSpan.fOther;
2431 int oCount = other->fTs.count();
2432 for (int o = 0; o < oCount; ++o) {
2433 Span& oSpan = other->fTs[o];
2434 if (oT == oSpan.fT && this == oSpan.fOther) {
2435 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002436 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002437 }
2438 }
2439 }
2440 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002441
caryclark@google.com495f8e42012-05-31 13:13:11 +00002442 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002443 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002444 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002445 SkASSERT(end >= 0);
2446 if (multipleSpans(end)) {
2447 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002448 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002449 const Span& endSpan = fTs[end];
2450 Segment* other = endSpan.fOther;
2451 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002452 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002453 Span* last = other->innerChaseDone(index, step, winding);
2454 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002455 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002456 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002457
caryclark@google.com59823f72012-08-09 18:17:47 +00002458 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002459 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002460 SkASSERT(end >= 0);
2461 if (multipleSpans(end)) {
2462 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002463 }
2464 const Span& endSpan = fTs[end];
2465 Segment* other = endSpan.fOther;
2466 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002467 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002468 int min = SkMin32(index, otherEnd);
2469 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002470 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002471 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002472 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002473 Span* last = other->innerChaseWinding(index, step, winding);
2474 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002475 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002476 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002477
caryclark@google.com235f56a2012-09-14 14:19:30 +00002478 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2479 fDoneSpans = 0;
2480 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002481 fPts = pts;
2482 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002483 }
2484
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002485 bool intersected() const {
2486 return fTs.count() > 0;
2487 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002488
2489 bool isConnected(int startIndex, int endIndex) const {
2490 return fTs[startIndex].fWindSum != SK_MinS32
2491 || fTs[endIndex].fWindSum != SK_MinS32;
2492 }
2493
caryclark@google.com235f56a2012-09-14 14:19:30 +00002494 bool isHorizontal() const {
2495 return fBounds.fTop == fBounds.fBottom;
2496 }
2497
caryclark@google.com15fa1382012-05-07 20:49:36 +00002498 bool isLinear(int start, int end) const {
2499 if (fVerb == SkPath::kLine_Verb) {
2500 return true;
2501 }
2502 if (fVerb == SkPath::kQuad_Verb) {
2503 SkPoint qPart[3];
2504 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2505 return QuadIsLinear(qPart);
2506 } else {
2507 SkASSERT(fVerb == SkPath::kCubic_Verb);
2508 SkPoint cPart[4];
2509 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2510 return CubicIsLinear(cPart);
2511 }
2512 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002513
2514 // OPTIMIZE: successive calls could start were the last leaves off
2515 // or calls could specialize to walk forwards or backwards
2516 bool isMissing(double startT) const {
2517 size_t tCount = fTs.count();
2518 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002519 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002520 return false;
2521 }
2522 }
2523 return true;
2524 }
2525
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002526 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002527 int count = fTs.count();
2528 if (count == 2) {
2529 return true;
2530 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002531 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002532 if (approximately_less_than_zero(t)) {
2533 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002534 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002535 if (approximately_greater_than_one(t)) {
2536 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002537 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002538 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002539 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002540
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002541 bool isVertical() const {
2542 return fBounds.fLeft == fBounds.fRight;
2543 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002544
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002545 SkScalar leftMost(int start, int end) const {
2546 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2547 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002548
caryclark@google.com495f8e42012-05-31 13:13:11 +00002549 // this span is excluded by the winding rule -- chase the ends
2550 // as long as they are unambiguous to mark connections as done
2551 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002552 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002553 int index = angle->start();
2554 int endIndex = angle->end();
2555 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002556 Span* last = innerChaseDone(index, step, winding);
2557 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002558 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002559 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002560
caryclark@google.com59823f72012-08-09 18:17:47 +00002561 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002562 int index = angle->start();
2563 int endIndex = angle->end();
2564 int min = SkMin32(index, endIndex);
2565 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002566 Span* last = innerChaseWinding(index, step, winding);
2567 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002568 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002569 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002570
caryclark@google.com495f8e42012-05-31 13:13:11 +00002571 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002572 // This may be called when the segment is already marked done. While this
2573 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002574 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002575 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002576 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002577 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002578 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002579 double referenceT = fTs[index].fT;
2580 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002581 #if PRECISE_T_SORT
2582 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2583 markOneDone(__FUNCTION__, lesser, winding);
2584 }
2585 do {
2586 markOneDone(__FUNCTION__, index, winding);
2587 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2588 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002589 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002590 markOneDone(__FUNCTION__, lesser, winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002591 }
2592 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002593 markOneDone(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002594 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00002595 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002596 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002597
caryclark@google.com24bec792012-08-20 12:43:57 +00002598 void markOneDone(const char* funName, int tIndex, int winding) {
2599 Span* span = markOneWinding(funName, tIndex, winding);
2600 if (!span) {
2601 return;
2602 }
2603 span->fDone = true;
2604 fDoneSpans++;
2605 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002606
caryclark@google.com24bec792012-08-20 12:43:57 +00002607 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2608 Span& span = fTs[tIndex];
2609 if (span.fDone) {
2610 return NULL;
2611 }
2612 #if DEBUG_MARK_DONE
2613 debugShowNewWinding(funName, span, winding);
2614 #endif
2615 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002616 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002617 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002618 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002619 span.fWindSum = winding;
2620 return &span;
2621 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002622
2623 void markUnsortable(int start, int end) {
2624 Span* span = &fTs[start];
2625 if (start < end) {
2626 span->fUnsortableStart = true;
2627 } else {
2628 --span;
2629 span->fUnsortableEnd = true;
2630 }
2631 if (span->fDone) {
2632 return;
2633 }
2634 span->fDone = true;
2635 fDoneSpans++;
2636 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002637
caryclark@google.com59823f72012-08-09 18:17:47 +00002638 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002639 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002640 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002641 double referenceT = fTs[index].fT;
2642 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002643 #if PRECISE_T_SORT
2644 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2645 markOneWinding(__FUNCTION__, lesser, winding);
2646 }
2647 do {
2648 markOneWinding(__FUNCTION__, index, winding);
2649 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2650 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002651 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002652 markOneWinding(__FUNCTION__, lesser, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002653 }
2654 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002655 markOneWinding(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002656 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00002657 #endif
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002658 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002659
caryclark@google.com2ddff932012-08-07 21:25:27 +00002660 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002661 int nextDoorWind = SK_MaxS32;
2662 if (tIndex > 0) {
2663 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002664 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002665 nextDoorWind = below.fWindValue;
2666 }
2667 }
2668 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2669 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002670 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002671 nextDoorWind = above.fWindValue;
2672 }
2673 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002674 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2675 const Span& below = fTs[tIndex - 1];
2676 nextDoorWind = below.fWindValue;
2677 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002678 if (nextDoorWind != SK_MaxS32) {
2679 Span& newSpan = fTs[tIndex];
2680 newSpan.fWindValue = nextDoorWind;
2681 if (!nextDoorWind) {
2682 newSpan.fDone = true;
2683 ++fDoneSpans;
2684 }
2685 }
2686 }
2687
caryclark@google.com9764cc62012-07-12 19:29:45 +00002688 // return span if when chasing, two or more radiating spans are not done
2689 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2690 // candidate and the remaining spans have windValue == 0 (canceled by
2691 // coincidence). The coincident edges could either be removed altogether,
2692 // or this code could be more complicated in detecting this case. Worth it?
2693 bool multipleSpans(int end) const {
2694 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002695 }
2696
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002697 // This has callers for two different situations: one establishes the end
2698 // of the current span, and one establishes the beginning of the next span
2699 // (thus the name). When this is looking for the end of the current span,
2700 // coincidence is found when the beginning Ts contain -step and the end
2701 // contains step. When it is looking for the beginning of the next, the
2702 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002703 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002704 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002705 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002706 int count = fTs.count();
2707 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002708 while (step > 0 ? ++to < count : --to >= 0) {
2709 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002710 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002711 continue;
2712 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002713 return to;
2714 }
2715 return -1;
2716 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00002717
caryclark@google.coma461ff02012-10-11 12:54:23 +00002718#if PRECISE_T_SORT
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002719 // FIXME
2720 // this returns at any difference in T, vs. a preset minimum. It may be
2721 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002722 // OPTIMIZATION splitting this into separate loops for up/down steps
2723 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002724 int nextExactSpan(int from, int step) const {
2725 const Span& fromSpan = fTs[from];
2726 int count = fTs.count();
2727 int to = from;
2728 while (step > 0 ? ++to < count : --to >= 0) {
2729 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00002730 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002731 continue;
2732 }
2733 return to;
2734 }
2735 return -1;
2736 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002737#endif
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002738
caryclark@google.com235f56a2012-09-14 14:19:30 +00002739 bool operand() const {
2740 return fOperand;
2741 }
2742
2743 int oppSign(int startIndex, int endIndex) const {
2744 int result = startIndex < endIndex ? -fTs[startIndex].fWindValueOpp :
2745 fTs[endIndex].fWindValueOpp;
2746#if DEBUG_WIND_BUMP
2747 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2748#endif
2749 return result;
2750 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002751
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002752 const SkPoint* pts() const {
2753 return fPts;
2754 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002755
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002756 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002757 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002758 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2759 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002760 }
2761
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002762 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
2763 int angleCount = angles.count();
2764 int angleIndex;
2765 angleList.setReserve(angleCount);
2766 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2767 *angleList.append() = &angles[angleIndex];
2768 }
2769 QSort<Angle>(angleList.begin(), angleList.end() - 1);
2770 bool result = true;
2771 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2772 Angle& angle = angles[angleIndex];
2773 if (angle.unsortable()) {
2774 // so that it is available for early exclusion in findTop and others
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002775 angle.segment()->markUnsortable(angle.start(), angle.end());
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002776 result = false;
2777 }
2778 }
2779 return result;
2780 }
2781
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002782 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002783 const Span& span(int tIndex) const {
2784 return fTs[tIndex];
2785 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002786
caryclark@google.com235f56a2012-09-14 14:19:30 +00002787 int spanSign(const Angle* angle) const {
2788 SkASSERT(angle->segment() == this);
2789 return spanSign(angle->start(), angle->end());
2790 }
2791
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002792 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002793 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue :
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002794 fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002795#if DEBUG_WIND_BUMP
2796 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2797#endif
2798 return result;
2799 }
2800
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002801 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002802 double t(int tIndex) const {
2803 return fTs[tIndex].fT;
2804 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002805
caryclark@google.com18063442012-07-25 12:05:18 +00002806 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
2807 double start) {
2808 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002809 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00002810 *outsideTs.append() = end;
2811 *outsideTs.append() = start;
2812 }
2813 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002814
caryclark@google.com24bec792012-08-20 12:43:57 +00002815 void undoneSpan(int& start, int& end) {
2816 size_t tCount = fTs.count();
2817 size_t index;
2818 for (index = 0; index < tCount; ++index) {
2819 if (!fTs[index].fDone) {
2820 break;
2821 }
2822 }
2823 SkASSERT(index < tCount - 1);
2824 start = index;
2825 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002826 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00002827 SkASSERT(index < tCount);
2828 SkASSERT(index < tCount);
2829 end = index;
2830 }
caryclark@google.com18063442012-07-25 12:05:18 +00002831
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002832 bool unsortable(int index) const {
2833 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
2834 }
2835
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002836 void updatePts(const SkPoint pts[]) {
2837 fPts = pts;
2838 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002839
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002840 SkPath::Verb verb() const {
2841 return fVerb;
2842 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002843
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002844 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002845 return fTs[tIndex].fWindSum;
2846 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002847
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002848 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002849 int start = angle->start();
2850 int end = angle->end();
2851 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002852 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002853 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002854
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002855 int windValue(int tIndex) const {
2856 return fTs[tIndex].fWindValue;
2857 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002858
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002859 int windValue(const Angle* angle) const {
2860 int start = angle->start();
2861 int end = angle->end();
2862 int index = SkMin32(start, end);
2863 return windValue(index);
2864 }
2865
2866 SkScalar xAtT(const Span* span) const {
2867 return xyAtT(span).fX;
2868 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002869
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002870 const SkPoint& xyAtT(int index) const {
2871 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002872 }
2873
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002874 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002875 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002876 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002877 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002878 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002879 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002880 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002881 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002882 }
2883 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002884 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002885 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002886
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002887 SkScalar yAtT(int index) const {
2888 return yAtT(&fTs[index]);
2889 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002890
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002891 SkScalar yAtT(const Span* span) const {
2892 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002893 }
2894
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002895#if DEBUG_DUMP
2896 void dump() const {
2897 const char className[] = "Segment";
2898 const int tab = 4;
2899 for (int i = 0; i < fTs.count(); ++i) {
2900 SkPoint out;
2901 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
2902 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002903 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002904 tab + sizeof(className), className, fID,
2905 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002906 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002907 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002908 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002909 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00002910 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002911 }
2912#endif
2913
caryclark@google.com47580692012-07-23 12:14:49 +00002914#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00002915 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002916 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00002917 for (int i = 0; i < fTs.count(); ++i) {
2918 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
2919 return;
2920 }
2921 }
2922 SkASSERT(0);
2923 }
2924#endif
2925
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002926#if DEBUG_DUMP
2927 int debugID() const {
2928 return fID;
2929 }
2930#endif
2931
caryclark@google.com24bec792012-08-20 12:43:57 +00002932#if DEBUG_WINDING
2933 void debugShowSums() const {
2934 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
2935 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
2936 for (int i = 0; i < fTs.count(); ++i) {
2937 const Span& span = fTs[i];
2938 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
2939 if (span.fWindSum == SK_MinS32) {
2940 SkDebugf("?");
2941 } else {
2942 SkDebugf("%d", span.fWindSum);
2943 }
2944 SkDebugf("]");
2945 }
2946 SkDebugf("\n");
2947 }
2948#endif
2949
caryclark@google.comcc905052012-07-25 20:59:42 +00002950#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002951 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00002952 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00002953 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00002954 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00002955 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
2956 }
2957 SkDebugf("\n");
2958 }
2959#endif
2960
caryclark@google.com027de222012-07-12 12:52:50 +00002961#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002962 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00002963 if (done()) {
2964 return;
2965 }
2966 for (int i = 0; i < fTs.count(); ++i) {
2967 if (fTs[i].fDone) {
2968 continue;
2969 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002970 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00002971 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2972 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2973 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2974 }
2975 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00002976 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00002977 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00002978 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002979 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
2980 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
2981 if (fTs[i].fWindSum == SK_MinS32) {
2982 SkDebugf("?");
2983 } else {
2984 SkDebugf("%d", fTs[i].fWindSum);
2985 }
2986 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00002987 }
2988 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002989
2990 // This isn't useful yet -- but leaving it in for now in case i think of something
2991 // to use it for
2992 void validateActiveSpans() const {
2993 if (done()) {
2994 return;
2995 }
2996 int tCount = fTs.count();
2997 for (int index = 0; index < tCount; ++index) {
2998 if (fTs[index].fDone) {
2999 continue;
3000 }
3001 // count number of connections which are not done
3002 int first = index;
3003 double baseT = fTs[index].fT;
3004 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3005 --first;
3006 }
3007 int last = index;
3008 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3009 ++last;
3010 }
3011 int connections = 0;
3012 connections += first > 0 && !fTs[first - 1].fDone;
3013 for (int test = first; test <= last; ++test) {
3014 connections += !fTs[test].fDone;
3015 const Segment* other = fTs[test].fOther;
3016 int oIndex = fTs[test].fOtherIndex;
3017 connections += !other->fTs[oIndex].fDone;
3018 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3019 }
3020 // SkASSERT(!(connections & 1));
3021 }
3022 }
caryclark@google.com027de222012-07-12 12:52:50 +00003023#endif
3024
caryclark@google.com0c803d02012-08-06 11:15:47 +00003025#if DEBUG_MARK_DONE
3026 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3027 const SkPoint& pt = xyAtT(&span);
3028 SkDebugf("%s id=%d", fun, fID);
3029 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3030 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3031 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3032 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003033 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3034 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3035 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3036 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003037 if (span.fWindSum == SK_MinS32) {
3038 SkDebugf("?");
3039 } else {
3040 SkDebugf("%d", span.fWindSum);
3041 }
3042 SkDebugf(" windValue=%d\n", span.fWindValue);
3043 }
3044#endif
3045
caryclark@google.com47580692012-07-23 12:14:49 +00003046#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003047 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003048 const int contourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003049 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003050 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003051 int lastSum = contourWinding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003052 int windSum = lastSum - spanSign(angles[first]);
caryclark@google.com03f97062012-08-21 13:13:52 +00003053 SkDebugf("%s %s contourWinding=%d sign=%d\n", fun, __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +00003054 contourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003055 int index = first;
3056 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003057 do {
3058 const Angle& angle = *angles[index];
3059 const Segment& segment = *angle.segment();
3060 int start = angle.start();
3061 int end = angle.end();
3062 const Span& sSpan = segment.fTs[start];
3063 const Span& eSpan = segment.fTs[end];
3064 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003065 if (!firstTime) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003066 lastSum = windSum;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003067 windSum -= segment.spanSign(&angle);
caryclark@google.comafe56de2012-07-24 18:11:03 +00003068 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003069 SkDebugf("%s [%d] %sid=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
3070 " sign=%d windValue=%d windSum=",
3071 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003072 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003073 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3074 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3075 mSpan.fWindValue);
3076 if (mSpan.fWindSum == SK_MinS32) {
3077 SkDebugf("?");
3078 } else {
3079 SkDebugf("%d", mSpan.fWindSum);
3080 }
3081 SkDebugf(" winding: %d->%d (max=%d) done=%d\n", lastSum, windSum,
3082 useInnerWinding(lastSum, windSum) ? windSum : lastSum,
3083 mSpan.fDone);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003084#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003085 angle.debugShow(segment.xyAtT(&sSpan));
3086#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003087 ++index;
3088 if (index == angles.count()) {
3089 index = 0;
3090 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003091 if (firstTime) {
3092 firstTime = false;
3093 }
caryclark@google.com47580692012-07-23 12:14:49 +00003094 } while (index != first);
3095 }
3096#endif
3097
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003098#if DEBUG_WINDING
3099 bool debugVerifyWinding(int start, int end, int winding) const {
3100 const Span& span = fTs[SkMin32(start, end)];
3101 int spanWinding = span.fWindSum;
3102 if (spanWinding == SK_MinS32) {
3103 return true;
3104 }
3105 int spanSign = SkSign32(start - end);
3106 int signedVal = spanSign * span.fWindValue;
3107 if (signedVal < 0) {
3108 spanWinding -= signedVal;
3109 }
3110 return span.fWindSum == winding;
3111 }
3112#endif
3113
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003114private:
3115 const SkPoint* fPts;
3116 SkPath::Verb fVerb;
3117 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003118 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00003119 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00003120 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003121#if DEBUG_DUMP
3122 int fID;
3123#endif
3124};
3125
caryclark@google.comb9738012012-07-03 19:53:30 +00003126class Contour;
3127
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003128struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003129 Contour* fContours[2];
3130 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003131 double fTs[2][2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00003132 bool fXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003133};
3134
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003135class Contour {
3136public:
3137 Contour() {
3138 reset();
3139#if DEBUG_DUMP
3140 fID = ++gContourID;
3141#endif
3142 }
3143
3144 bool operator<(const Contour& rh) const {
3145 return fBounds.fTop == rh.fBounds.fTop
3146 ? fBounds.fLeft < rh.fBounds.fLeft
3147 : fBounds.fTop < rh.fBounds.fTop;
3148 }
3149
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003150 void addCoincident(int index, Contour* other, int otherIndex,
3151 const Intersections& ts, bool swap) {
3152 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00003153 coincidence.fContours[0] = this;
3154 coincidence.fContours[1] = other;
3155 coincidence.fSegments[0] = index;
3156 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003157 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3158 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3159 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3160 coincidence.fTs[swap][0] = ts.fT[0][0];
3161 coincidence.fTs[swap][1] = ts.fT[0][1];
3162 coincidence.fTs[!swap][0] = ts.fT[1][0];
3163 coincidence.fTs[!swap][1] = ts.fT[1][1];
3164 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3165 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3166 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3167 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3168 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3169 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3170 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003171 coincidence.fXor = fOperand == other->fOperand ? fXor : true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003172 }
3173
3174 void addCross(const Contour* crosser) {
3175#ifdef DEBUG_CROSS
3176 for (int index = 0; index < fCrosses.count(); ++index) {
3177 SkASSERT(fCrosses[index] != crosser);
3178 }
3179#endif
3180 *fCrosses.append() = crosser;
3181 }
3182
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003183 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003184 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003185 fContainsCurves = true;
3186 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003187
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003188 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003189 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003190 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003191 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003192
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003193 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3194 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3195 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003196
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003197 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003198 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003199 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003200 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003201 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003202
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003203 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3204 containsIntercepts();
3205 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3206 }
3207
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003208 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003209 return fBounds;
3210 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003211
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003212 void collapseTriangles() {
3213 int segmentCount = fSegments.count();
3214 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3215 fSegments[sIndex].collapseTriangles(fXor);
3216 }
3217 }
3218
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003219 void complete() {
3220 setBounds();
3221 fContainsIntercepts = false;
3222 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003223
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003224 void containsIntercepts() {
3225 fContainsIntercepts = true;
3226 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003227
rmistry@google.comd6176b02012-08-23 18:14:13 +00003228 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003229 int &tIndex, double& hitT) {
3230 int segmentCount = fSegments.count();
3231 const Segment* bestSegment = NULL;
3232 for (int test = 0; test < segmentCount; ++test) {
3233 Segment* testSegment = &fSegments[test];
3234 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003235 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003236 continue;
3237 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003238 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003239 continue;
3240 }
3241 if (bounds.fLeft > basePt.fX) {
3242 continue;
3243 }
3244 if (bounds.fRight < basePt.fX) {
3245 continue;
3246 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003247 if (bounds.fLeft == bounds.fRight) {
3248 continue;
3249 }
3250 #if 0
3251 bool leftHalf = bounds.fLeft == basePt.fX;
3252 bool rightHalf = bounds.fRight == basePt.fX;
3253 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3254 basePt, leftHalf, rightHalf)) {
3255 continue;
3256 }
3257 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003258 double testHitT;
3259 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3260 if (testT >= 0) {
3261 bestSegment = testSegment;
3262 tIndex = testT;
3263 hitT = testHitT;
3264 }
3265 }
3266 return bestSegment;
3267 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003268
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003269 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003270 for (int index = 0; index < fCrosses.count(); ++index) {
3271 if (fCrosses[index] == crosser) {
3272 return true;
3273 }
3274 }
3275 return false;
3276 }
3277
caryclark@google.com235f56a2012-09-14 14:19:30 +00003278 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003279 int segmentCount = fSegments.count();
3280 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003281 fSegments[sIndex].findTooCloseToCall(fXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003282 }
3283 }
3284
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003285 void fixOtherTIndex() {
3286 int segmentCount = fSegments.count();
3287 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3288 fSegments[sIndex].fixOtherTIndex();
3289 }
3290 }
3291
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003292 void reset() {
3293 fSegments.reset();
3294 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003295 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003296 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003297
caryclark@google.com235f56a2012-09-14 14:19:30 +00003298 // FIXME: for binary ops, need to keep both ops winding contributions separately
3299 // in edge array
3300 void resolveCoincidence() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003301 int count = fCoincidences.count();
3302 for (int index = 0; index < count; ++index) {
3303 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003304 Contour* thisContour = coincidence.fContours[0];
3305 Contour* otherContour = coincidence.fContours[1];
3306 int thisIndex = coincidence.fSegments[0];
3307 int otherIndex = coincidence.fSegments[1];
3308 Segment& thisOne = thisContour->fSegments[thisIndex];
3309 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003310 #if DEBUG_CONCIDENT
3311 thisOne.debugShowTs();
3312 other.debugShowTs();
3313 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003314 double startT = coincidence.fTs[0][0];
3315 double endT = coincidence.fTs[0][1];
caryclark@google.com32546db2012-08-31 20:55:07 +00003316 bool opposite = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003317 if (startT > endT) {
3318 SkTSwap<double>(startT, endT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003319 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003320 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003321 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003322 double oStartT = coincidence.fTs[1][0];
3323 double oEndT = coincidence.fTs[1][1];
3324 if (oStartT > oEndT) {
3325 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003326 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003327 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003328 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com32546db2012-08-31 20:55:07 +00003329 if (opposite) {
caryclark@google.comb9738012012-07-03 19:53:30 +00003330 // make sure startT and endT have t entries
caryclark@google.com32546db2012-08-31 20:55:07 +00003331 SkASSERT(opposite);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003332 if (startT > 0 || oEndT < 1
3333 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3334 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003335 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003336 if (oStartT > 0 || endT < 1
3337 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3338 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003339 }
3340 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003341 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003342 if (startT > 0 || oStartT > 0
3343 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003344 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003345 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003346 if (endT < 1 || oEndT < 1
3347 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003348 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003349 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003350 thisOne.addTCoincident(coincidence.fXor, startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003351 }
caryclark@google.com47580692012-07-23 12:14:49 +00003352 #if DEBUG_CONCIDENT
3353 thisOne.debugShowTs();
3354 other.debugShowTs();
3355 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003356 }
3357 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003358
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003359 const SkTArray<Segment>& segments() {
3360 return fSegments;
3361 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003362
caryclark@google.com235f56a2012-09-14 14:19:30 +00003363 void setOperand(bool isOp) {
3364 fOperand = isOp;
3365 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003366
caryclark@google.com235f56a2012-09-14 14:19:30 +00003367 void setXor(bool isXor) {
3368 fXor = isXor;
3369 }
3370
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003371#if !SORTABLE_CONTOURS
3372 void sortSegments() {
3373 int segmentCount = fSegments.count();
3374 fSortedSegments.setReserve(segmentCount);
3375 for (int test = 0; test < segmentCount; ++test) {
3376 *fSortedSegments.append() = &fSegments[test];
3377 }
3378 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
3379 fFirstSorted = 0;
3380 }
3381#endif
3382
caryclark@google.com15fa1382012-05-07 20:49:36 +00003383 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3384 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003385 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003386 // segments' top, and not a true sort, so it could be ameniable to regular
3387 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003388 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003389 int segmentCount = fSegments.count();
3390 SkASSERT(segmentCount > 0);
3391 int best = -1;
3392 Segment* bestSegment = NULL;
3393 while (++best < segmentCount) {
3394 Segment* testSegment = &fSegments[best];
3395 if (testSegment->done()) {
3396 continue;
3397 }
3398 bestSegment = testSegment;
3399 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003400 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003401 if (!bestSegment) {
3402 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003403 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003404 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003405 for (int test = best + 1; test < segmentCount; ++test) {
3406 Segment* testSegment = &fSegments[test];
3407 if (testSegment->done()) {
3408 continue;
3409 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003410 if (testSegment->bounds().fTop > bestTop) {
3411 continue;
3412 }
3413 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003414 if (bestTop > testTop) {
3415 bestTop = testTop;
3416 bestSegment = testSegment;
3417 }
3418 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003419 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003420 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003421 }
3422
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003423#if !SORTABLE_CONTOURS
3424 Segment* topSortableSegment(SkScalar& bestY) {
3425 int segmentCount = fSortedSegments.count();
3426 SkASSERT(segmentCount > 0);
3427 Segment* bestSegment = NULL;
3428 while (fFirstSorted < segmentCount) {
3429 Segment* testSegment = fSortedSegments[fFirstSorted];
3430 if (testSegment->done()) {
3431 fFirstSorted++;
3432 continue;
3433 }
3434 bestSegment = testSegment;
3435 break;
3436 }
3437 if (!bestSegment) {
3438 return NULL;
3439 }
3440 bestY = bestSegment->activeTop();
3441 return bestSegment;
3442 }
3443#endif
3444
caryclark@google.com24bec792012-08-20 12:43:57 +00003445 Segment* undoneSegment(int& start, int& end) {
3446 int segmentCount = fSegments.count();
3447 for (int test = 0; test < segmentCount; ++test) {
3448 Segment* testSegment = &fSegments[test];
3449 if (testSegment->done()) {
3450 continue;
3451 }
3452 testSegment->undoneSpan(start, end);
3453 return testSegment;
3454 }
3455 return NULL;
3456 }
3457
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003458 int updateSegment(int index, const SkPoint* pts) {
3459 Segment& segment = fSegments[index];
3460 segment.updatePts(pts);
3461 return segment.verb() + 1;
3462 }
3463
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003464#if DEBUG_TEST
3465 SkTArray<Segment>& debugSegments() {
3466 return fSegments;
3467 }
3468#endif
3469
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003470#if DEBUG_DUMP
3471 void dump() {
3472 int i;
3473 const char className[] = "Contour";
3474 const int tab = 4;
3475 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
3476 for (i = 0; i < fSegments.count(); ++i) {
3477 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
3478 className, i);
3479 fSegments[i].dump();
3480 }
3481 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
3482 tab + sizeof(className), className,
3483 fBounds.fLeft, fBounds.fTop,
3484 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003485 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
3486 className, fContainsIntercepts);
3487 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
3488 className, fContainsCurves);
3489 }
3490#endif
3491
caryclark@google.com027de222012-07-12 12:52:50 +00003492#if DEBUG_ACTIVE_SPANS
3493 void debugShowActiveSpans() {
3494 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003495 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00003496 }
3497 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003498
3499 void validateActiveSpans() {
3500 for (int index = 0; index < fSegments.count(); ++index) {
3501 fSegments[index].validateActiveSpans();
3502 }
3503 }
caryclark@google.com027de222012-07-12 12:52:50 +00003504#endif
3505
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003506protected:
3507 void setBounds() {
3508 int count = fSegments.count();
3509 if (count == 0) {
3510 SkDebugf("%s empty contour\n", __FUNCTION__);
3511 SkASSERT(0);
3512 // FIXME: delete empty contour?
3513 return;
3514 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003515 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003516 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003517 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003518 }
3519 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003520
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003521private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003522 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003523#if !SORTABLE_CONTOURS
3524 SkTDArray<Segment*> fSortedSegments;
3525 int fFirstSorted;
3526#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003527 SkTDArray<Coincidence> fCoincidences;
3528 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003529 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003530 bool fContainsIntercepts;
3531 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003532 bool fOperand; // true for the second argument to a binary operator
3533 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003534#if DEBUG_DUMP
3535 int fID;
3536#endif
3537};
3538
3539class EdgeBuilder {
3540public:
3541
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003542EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003543 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003544 , fContours(contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003545 , fCurrentContour(NULL)
3546 , fOperand(false)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003547{
caryclark@google.com235f56a2012-09-14 14:19:30 +00003548 fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003549#if DEBUG_DUMP
3550 gContourID = 0;
3551 gSegmentID = 0;
3552#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00003553 fSecondHalf = preFetch();
3554}
3555
3556void addOperand(const SkPath& path) {
3557 fPath = &path;
3558 fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
3559 preFetch();
3560}
3561
3562void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003563 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003564 complete();
3565 if (fCurrentContour && !fCurrentContour->segments().count()) {
3566 fContours.pop_back();
3567 }
3568 // correct pointers in contours since fReducePts may have moved as it grew
3569 int cIndex = 0;
3570 int extraCount = fExtra.count();
3571 SkASSERT(extraCount == 0 || fExtra[0] == -1);
3572 int eIndex = 0;
3573 int rIndex = 0;
3574 while (++eIndex < extraCount) {
3575 int offset = fExtra[eIndex];
3576 if (offset < 0) {
3577 ++cIndex;
3578 continue;
3579 }
3580 fCurrentContour = &fContours[cIndex];
3581 rIndex += fCurrentContour->updateSegment(offset - 1,
3582 &fReducePts[rIndex]);
3583 }
3584 fExtra.reset(); // we're done with this
3585}
3586
3587ShapeOpMask xorMask() const {
3588 return fXorMask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003589}
3590
3591protected:
3592
3593void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003594 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003595 fCurrentContour->complete();
3596 fCurrentContour = NULL;
3597 }
3598}
3599
caryclark@google.com235f56a2012-09-14 14:19:30 +00003600// FIXME:remove once we can access path pts directly
3601int preFetch() {
3602 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003603 SkPoint pts[4];
3604 SkPath::Verb verb;
3605 do {
3606 verb = iter.next(pts);
3607 *fPathVerbs.append() = verb;
3608 if (verb == SkPath::kMove_Verb) {
3609 *fPathPts.append() = pts[0];
3610 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
3611 fPathPts.append(verb, &pts[1]);
3612 }
3613 } while (verb != SkPath::kDone_Verb);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003614 return fPathVerbs.count();
3615}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003616
caryclark@google.com235f56a2012-09-14 14:19:30 +00003617void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003618 SkPath::Verb reducedVerb;
3619 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003620 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003621 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003622 const SkPoint* finalCurveStart = NULL;
3623 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003624 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003625 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
3626 switch (verb) {
3627 case SkPath::kMove_Verb:
3628 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003629 if (!fCurrentContour) {
3630 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003631 fCurrentContour->setOperand(fOperand);
3632 fCurrentContour->setXor(fXorMask == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003633 *fExtra.append() = -1; // start new contour
3634 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003635 finalCurveEnd = pointsPtr++;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003636 continue;
3637 case SkPath::kLine_Verb:
3638 // skip degenerate points
3639 if (pointsPtr[-1].fX != pointsPtr[0].fX
3640 || pointsPtr[-1].fY != pointsPtr[0].fY) {
3641 fCurrentContour->addLine(&pointsPtr[-1]);
3642 }
3643 break;
3644 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00003645
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003646 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
3647 if (reducedVerb == 0) {
3648 break; // skip degenerate points
3649 }
3650 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00003651 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003652 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003653 break;
3654 }
3655 fCurrentContour->addQuad(&pointsPtr[-1]);
3656 break;
3657 case SkPath::kCubic_Verb:
3658 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
3659 if (reducedVerb == 0) {
3660 break; // skip degenerate points
3661 }
3662 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003663 *fExtra.append() =
3664 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003665 break;
3666 }
3667 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003668 *fExtra.append() =
3669 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003670 break;
3671 }
3672 fCurrentContour->addCubic(&pointsPtr[-1]);
3673 break;
3674 case SkPath::kClose_Verb:
3675 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003676 if (finalCurveStart && finalCurveEnd
3677 && *finalCurveStart != *finalCurveEnd) {
3678 *fReducePts.append() = *finalCurveStart;
3679 *fReducePts.append() = *finalCurveEnd;
3680 *fExtra.append() =
3681 fCurrentContour->addLine(fReducePts.end() - 2);
3682 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003683 complete();
3684 continue;
3685 default:
3686 SkDEBUGFAIL("bad verb");
3687 return;
3688 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003689 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003690 pointsPtr += verb;
3691 SkASSERT(fCurrentContour);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003692 if (verbPtr == endOfFirstHalf) {
3693 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003694 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003695 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003696}
3697
3698private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00003699 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003700 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003701 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003702 Contour* fCurrentContour;
3703 SkTArray<Contour>& fContours;
3704 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003705 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com235f56a2012-09-14 14:19:30 +00003706 ShapeOpMask fXorMask;
3707 int fSecondHalf;
3708 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003709};
3710
3711class Work {
3712public:
3713 enum SegmentType {
3714 kHorizontalLine_Segment = -1,
3715 kVerticalLine_Segment = 0,
3716 kLine_Segment = SkPath::kLine_Verb,
3717 kQuad_Segment = SkPath::kQuad_Verb,
3718 kCubic_Segment = SkPath::kCubic_Verb,
3719 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00003720
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003721 void addCoincident(Work& other, const Intersections& ts, bool swap) {
3722 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
3723 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003724
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003725 // FIXME: does it make sense to write otherIndex now if we're going to
3726 // fix it up later?
3727 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003728 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003729 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003730
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003731 // Avoid collapsing t values that are close to the same since
3732 // we walk ts to describe consecutive intersections. Since a pair of ts can
3733 // be nearly equal, any problems caused by this should be taken care
3734 // of later.
3735 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003736 int addT(double newT, const Work& other) {
3737 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003738 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003739
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003740 bool advance() {
3741 return ++fIndex < fLast;
3742 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003743
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003744 SkScalar bottom() const {
3745 return bounds().fBottom;
3746 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003747
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003748 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003749 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003750 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003751
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003752 const SkPoint* cubic() const {
3753 return fCubic;
3754 }
3755
3756 void init(Contour* contour) {
3757 fContour = contour;
3758 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003759 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003760 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003761
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003762 bool isAdjacent(const Work& next) {
3763 return fContour == next.fContour && fIndex + 1 == next.fIndex;
3764 }
3765
3766 bool isFirstLast(const Work& next) {
3767 return fContour == next.fContour && fIndex == 0
3768 && next.fIndex == fLast - 1;
3769 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003770
3771 SkScalar left() const {
3772 return bounds().fLeft;
3773 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003774
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003775 void promoteToCubic() {
3776 fCubic[0] = pts()[0];
3777 fCubic[2] = pts()[1];
3778 fCubic[3] = pts()[2];
3779 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
3780 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
3781 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
3782 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
3783 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003784
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003785 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003786 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003787 }
3788
3789 SkScalar right() const {
3790 return bounds().fRight;
3791 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003792
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003793 ptrdiff_t segmentIndex() const {
3794 return fIndex;
3795 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003796
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003797 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003798 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003799 SegmentType type = (SegmentType) segment.verb();
3800 if (type != kLine_Segment) {
3801 return type;
3802 }
3803 if (segment.isHorizontal()) {
3804 return kHorizontalLine_Segment;
3805 }
3806 if (segment.isVertical()) {
3807 return kVerticalLine_Segment;
3808 }
3809 return kLine_Segment;
3810 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003811
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003812 bool startAfter(const Work& after) {
3813 fIndex = after.fIndex;
3814 return advance();
3815 }
3816
3817 SkScalar top() const {
3818 return bounds().fTop;
3819 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003820
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003821 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003822 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003823 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003824
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003825 SkScalar x() const {
3826 return bounds().fLeft;
3827 }
3828
3829 bool xFlipped() const {
3830 return x() != pts()[0].fX;
3831 }
3832
3833 SkScalar y() const {
3834 return bounds().fTop;
3835 }
3836
3837 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003838 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003839 }
3840
3841protected:
3842 Contour* fContour;
3843 SkPoint fCubic[4];
3844 int fIndex;
3845 int fLast;
3846};
3847
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003848#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003849static void debugShowLineIntersection(int pts, const Work& wt,
3850 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003851 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003852 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003853 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
3854 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
3855 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
3856 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003857 return;
3858 }
3859 SkPoint wtOutPt, wnOutPt;
3860 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3861 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00003862 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 +00003863 __FUNCTION__,
3864 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3865 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
3866 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00003867 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003868 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00003869 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003870 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3871 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
3872 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00003873 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
3874 }
3875 SkDebugf("\n");
3876}
3877
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003878static void debugShowQuadLineIntersection(int pts, const Work& wt,
3879 const Work& wn, const double wtTs[2], const double wnTs[2]) {
3880 if (!pts) {
3881 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
3882 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
3883 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
3884 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
3885 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
3886 wt.pts()[2].fX, wt.pts()[2].fY );
3887 return;
3888 }
3889 SkPoint wtOutPt, wnOutPt;
3890 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3891 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
3892 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
3893 __FUNCTION__,
3894 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3895 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
3896 wtOutPt.fX, wtOutPt.fY);
3897 if (pts == 2) {
3898 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
3899 }
3900 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
3901 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3902 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
3903 if (pts == 2) {
3904 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
3905 }
3906 SkDebugf("\n");
3907}
3908
caryclark@google.coma461ff02012-10-11 12:54:23 +00003909static void debugShowQuadIntersection(int pts, const Work& wt,
3910 const Work& wn, const double wtTs[2], const double wnTs[2]) {
3911 if (!pts) {
3912 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
3913 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
3914 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00003915 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00003916 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
3917 wt.pts()[2].fX, wt.pts()[2].fY );
3918 return;
3919 }
3920 SkPoint wtOutPt, wnOutPt;
3921 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3922 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
3923 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
3924 __FUNCTION__,
3925 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3926 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
3927 wtOutPt.fX, wtOutPt.fY);
3928 if (pts == 2) {
3929 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
3930 }
3931 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
3932 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3933 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
3934 wnOutPt.fX, wnOutPt.fY);
3935 if (pts == 2) {
3936 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003937 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003938 SkDebugf("\n");
3939}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003940#else
3941static void debugShowLineIntersection(int , const Work& ,
3942 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003943}
caryclark@google.coma461ff02012-10-11 12:54:23 +00003944
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003945static void debugShowQuadLineIntersection(int , const Work& ,
3946 const Work& , const double [2], const double [2]) {
3947}
3948
caryclark@google.coma461ff02012-10-11 12:54:23 +00003949static void debugShowQuadIntersection(int , const Work& ,
3950 const Work& , const double [2], const double [2]) {
3951}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003952#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003953
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003954static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003955
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003956 if (test != next) {
3957 if (test->bounds().fBottom < next->bounds().fTop) {
3958 return false;
3959 }
3960 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
3961 return true;
3962 }
3963 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003964 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003965 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003966 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003967 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003968 Work wn;
3969 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003970 if (test == next && !wn.startAfter(wt)) {
3971 continue;
3972 }
3973 do {
3974 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
3975 continue;
3976 }
3977 int pts;
3978 Intersections ts;
3979 bool swap = false;
3980 switch (wt.segmentType()) {
3981 case Work::kHorizontalLine_Segment:
3982 swap = true;
3983 switch (wn.segmentType()) {
3984 case Work::kHorizontalLine_Segment:
3985 case Work::kVerticalLine_Segment:
3986 case Work::kLine_Segment: {
3987 pts = HLineIntersect(wn.pts(), wt.left(),
3988 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003989 debugShowLineIntersection(pts, wt, wn,
3990 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003991 break;
3992 }
3993 case Work::kQuad_Segment: {
3994 pts = HQuadIntersect(wn.pts(), wt.left(),
3995 wt.right(), wt.y(), wt.xFlipped(), ts);
3996 break;
3997 }
3998 case Work::kCubic_Segment: {
3999 pts = HCubicIntersect(wn.pts(), wt.left(),
4000 wt.right(), wt.y(), wt.xFlipped(), ts);
4001 break;
4002 }
4003 default:
4004 SkASSERT(0);
4005 }
4006 break;
4007 case Work::kVerticalLine_Segment:
4008 swap = true;
4009 switch (wn.segmentType()) {
4010 case Work::kHorizontalLine_Segment:
4011 case Work::kVerticalLine_Segment:
4012 case Work::kLine_Segment: {
4013 pts = VLineIntersect(wn.pts(), wt.top(),
4014 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004015 debugShowLineIntersection(pts, wt, wn,
4016 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004017 break;
4018 }
4019 case Work::kQuad_Segment: {
4020 pts = VQuadIntersect(wn.pts(), wt.top(),
4021 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4022 break;
4023 }
4024 case Work::kCubic_Segment: {
4025 pts = VCubicIntersect(wn.pts(), wt.top(),
4026 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4027 break;
4028 }
4029 default:
4030 SkASSERT(0);
4031 }
4032 break;
4033 case Work::kLine_Segment:
4034 switch (wn.segmentType()) {
4035 case Work::kHorizontalLine_Segment:
4036 pts = HLineIntersect(wt.pts(), wn.left(),
4037 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004038 debugShowLineIntersection(pts, wt, wn,
4039 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004040 break;
4041 case Work::kVerticalLine_Segment:
4042 pts = VLineIntersect(wt.pts(), wn.top(),
4043 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004044 debugShowLineIntersection(pts, wt, wn,
4045 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004046 break;
4047 case Work::kLine_Segment: {
4048 pts = LineIntersect(wt.pts(), wn.pts(), ts);
4049 debugShowLineIntersection(pts, wt, wn,
4050 ts.fT[1], ts.fT[0]);
4051 break;
4052 }
4053 case Work::kQuad_Segment: {
4054 swap = true;
4055 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004056 debugShowQuadLineIntersection(pts, wt, wn,
4057 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004058 break;
4059 }
4060 case Work::kCubic_Segment: {
4061 swap = true;
4062 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
4063 break;
4064 }
4065 default:
4066 SkASSERT(0);
4067 }
4068 break;
4069 case Work::kQuad_Segment:
4070 switch (wn.segmentType()) {
4071 case Work::kHorizontalLine_Segment:
4072 pts = HQuadIntersect(wt.pts(), wn.left(),
4073 wn.right(), wn.y(), wn.xFlipped(), ts);
4074 break;
4075 case Work::kVerticalLine_Segment:
4076 pts = VQuadIntersect(wt.pts(), wn.top(),
4077 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4078 break;
4079 case Work::kLine_Segment: {
4080 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004081 debugShowQuadLineIntersection(pts, wt, wn,
4082 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004083 break;
4084 }
4085 case Work::kQuad_Segment: {
4086 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004087 debugShowQuadIntersection(pts, wt, wn,
4088 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004089 break;
4090 }
4091 case Work::kCubic_Segment: {
4092 wt.promoteToCubic();
4093 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
4094 break;
4095 }
4096 default:
4097 SkASSERT(0);
4098 }
4099 break;
4100 case Work::kCubic_Segment:
4101 switch (wn.segmentType()) {
4102 case Work::kHorizontalLine_Segment:
4103 pts = HCubicIntersect(wt.pts(), wn.left(),
4104 wn.right(), wn.y(), wn.xFlipped(), ts);
4105 break;
4106 case Work::kVerticalLine_Segment:
4107 pts = VCubicIntersect(wt.pts(), wn.top(),
4108 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4109 break;
4110 case Work::kLine_Segment: {
4111 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
4112 break;
4113 }
4114 case Work::kQuad_Segment: {
4115 wn.promoteToCubic();
4116 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
4117 break;
4118 }
4119 case Work::kCubic_Segment: {
4120 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4121 break;
4122 }
4123 default:
4124 SkASSERT(0);
4125 }
4126 break;
4127 default:
4128 SkASSERT(0);
4129 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004130 if (!foundCommonContour && pts > 0) {
4131 test->addCross(next);
4132 next->addCross(test);
4133 foundCommonContour = true;
4134 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004135 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004136 if (pts == 2) {
4137 if (wn.segmentType() <= Work::kLine_Segment
4138 && wt.segmentType() <= Work::kLine_Segment) {
4139 wt.addCoincident(wn, ts, swap);
4140 continue;
4141 }
4142 if (wn.segmentType() == Work::kQuad_Segment
4143 && wt.segmentType() == Work::kQuad_Segment
4144 && ts.coincidentUsed() == 2) {
4145 wt.addCoincident(wn, ts, swap);
4146 continue;
4147 }
4148
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004149 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004150 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004151 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4152 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004153 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4154 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004155 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4156 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004157 }
4158 } while (wn.advance());
4159 } while (wt.advance());
4160 return true;
4161}
4162
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004163// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004164// see if coincidence is formed by clipping non-concident segments
caryclark@google.com235f56a2012-09-14 14:19:30 +00004165static void coincidenceCheck(SkTDArray<Contour*>& contourList) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004166 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004167 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004168 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004169 contour->resolveCoincidence();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004170 }
4171 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4172 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004173 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004174 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004175#if 0
4176 // OPTIMIZATION: this check could be folded in with findTooClose -- maybe
4177 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4178 Contour* contour = contourList[cIndex];
4179 contour->collapseTriangles();
4180 }
4181#endif
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004182}
4183
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004184// project a ray from the top of the contour up and see if it hits anything
4185// note: when we compute line intersections, we keep track of whether
4186// two contours touch, so we need only look at contours not touching this one.
4187// OPTIMIZATION: sort contourList vertically to avoid linear walk
4188static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004189 const Segment* current, int index, int endIndex) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004190 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004191 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004192 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00004193 const Segment* test = NULL;
4194 int tIndex;
4195 double tHit;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004196 // bool checkCrosses = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004197 for (int cTest = 0; cTest < contourCount; ++cTest) {
4198 Contour* contour = contourList[cTest];
4199 if (basePt.fY < contour->bounds().fTop) {
4200 continue;
4201 }
4202 if (bestY > contour->bounds().fBottom) {
4203 continue;
4204 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004205#if 0
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004206 // even though the contours crossed, if spans cancel through concidence,
4207 // the contours may be not have any span links to chase, and the current
4208 // segment may be isolated. Detect this by seeing if current has
4209 // uninitialized wind sums. If so, project a ray instead of relying on
4210 // previously found intersections.
4211 if (baseContour == contour) {
4212 continue;
4213 }
4214 if (checkCrosses && baseContour->crosses(contour)) {
4215 if (current->isConnected(index, endIndex)) {
4216 continue;
4217 }
4218 checkCrosses = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004219 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004220#endif
caryclark@google.com47580692012-07-23 12:14:49 +00004221 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
4222 if (next) {
4223 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004224 }
caryclark@google.com47580692012-07-23 12:14:49 +00004225 }
4226 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00004227 return 0;
4228 }
4229 int winding, windValue;
4230 // If the ray hit the end of a span, we need to construct the wheel of
4231 // angles to find the span closest to the ray -- even if there are just
4232 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004233 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004234 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00004235 SkTDArray<Angle> angles;
4236 int end = test->nextSpan(tIndex, 1);
4237 if (end < 0) {
4238 end = test->nextSpan(tIndex, -1);
4239 }
4240 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00004241 SkASSERT(angles.count() > 0);
4242 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
4243#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00004244 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00004245#endif
4246 return 0;
4247 }
caryclark@google.com47580692012-07-23 12:14:49 +00004248 test->buildAngles(tIndex, angles);
4249 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00004250 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00004251 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004252 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00004253 // hour after 6 o'clock
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004254 (void) Segment::SortAngles(angles, sorted);
caryclark@google.com47580692012-07-23 12:14:49 +00004255#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004256 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00004257#endif
4258 // walk the sorted angle fan to find the lowest angle
4259 // above the base point. Currently, the first angle in the sorted array
4260 // is 12 noon or an earlier hour (the next counterclockwise)
4261 int count = sorted.count();
4262 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004263 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00004264 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004265 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00004266 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00004267 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004268 if (angle->unsortable()) {
4269 continue;
4270 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004271 if (baseMatches && angle->isHorizontal()) {
4272 continue;
4273 }
4274 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00004275 test = angle->segment();
4276 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
4277 const SkPoint* pts = test->pts();
4278 indexDx = pts[2].fX - pts[1].fX - indexDx;
4279 }
caryclark@google.com47580692012-07-23 12:14:49 +00004280 if (indexDx < 0) {
4281 left = index;
4282 } else if (indexDx > 0) {
4283 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00004284 int previous = index - 1;
4285 if (previous < 0) {
4286 previous = count - 1;
4287 }
4288 const Angle* prev = sorted[previous];
4289 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
4290#if DEBUG_SORT
4291 SkDebugf("%s use prev\n", __FUNCTION__);
4292#endif
4293 right = previous;
4294 }
caryclark@google.com47580692012-07-23 12:14:49 +00004295 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004296 } else {
4297 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004298 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004299 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004300 if (left < 0 && right < 0) {
4301 left = mid;
4302 }
caryclark@google.com47580692012-07-23 12:14:49 +00004303 SkASSERT(left >= 0 || right >= 0);
4304 if (left < 0) {
4305 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004306 } else if (left >= 0 && mid >= 0 && right >= 0
4307 && sorted[mid]->sign() == sorted[right]->sign()) {
4308 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004309 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004310 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004311 test = angle->segment();
4312 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004313 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004314 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004315#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004316 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4317 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004318#endif
4319 } else {
4320 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004321 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004322 windValue = test->windValue(tIndex);
4323#if DEBUG_WINDING
4324 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4325 windValue);
4326#endif
4327 }
4328 // see if a + change in T results in a +/- change in X (compute x'(T))
4329 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004330 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4331 const SkPoint* pts = test->pts();
4332 dx = pts[2].fX - pts[1].fX - dx;
4333 }
caryclark@google.com47580692012-07-23 12:14:49 +00004334#if DEBUG_WINDING
4335 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4336#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004337 SkASSERT(dx != 0);
4338 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004339 winding += dx > 0 ? -windValue : windValue;
4340#if DEBUG_WINDING
4341 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4342#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004343 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004344 return winding;
4345}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004346
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004347#if SORTABLE_CONTOURS
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004348// OPTIMIZATION: not crazy about linear search here to find top active y.
4349// seems like we should break down and do the sort, or maybe sort each
rmistry@google.comd6176b02012-08-23 18:14:13 +00004350// contours' segments?
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004351// Once the segment array is built, there's no reason I can think of not to
4352// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004353// FIXME: return the contour found to pass to inner contour check
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004354static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004355 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004356 int cIndex = 0;
4357 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004358 SkScalar bestY = SK_ScalarMax;
4359 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004360 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004361 contour = contourList[cIndex];
4362 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004363 } while (!topStart && ++cIndex < contourCount);
4364 if (!topStart) {
4365 return NULL;
4366 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004367 while (++cIndex < contourCount) {
4368 contour = contourList[cIndex];
4369 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004370 continue;
4371 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004372 SkScalar testY = SK_ScalarMax;
4373 Segment* test = contour->topSegment(testY);
4374 if (!test || bestY <= testY) {
4375 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004376 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004377 topStart = test;
4378 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004379 }
4380 return topStart;
4381}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004382#endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004383
caryclark@google.com24bec792012-08-20 12:43:57 +00004384static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4385 int contourCount = contourList.count();
4386 Segment* result;
4387 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4388 Contour* contour = contourList[cIndex];
4389 result = contour->undoneSegment(start, end);
4390 if (result) {
4391 return result;
4392 }
4393 }
4394 return NULL;
4395}
4396
4397
4398
caryclark@google.come21cb182012-07-23 21:26:31 +00004399static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
4400 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004401 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004402 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004403 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4404 Segment* segment = backPtr.fOther;
4405 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004406 SkTDArray<Angle> angles;
4407 int done = 0;
4408 if (segment->activeAngle(tIndex, done, angles)) {
4409 Angle* last = angles.end() - 1;
4410 tIndex = last->start();
4411 endIndex = last->end();
4412 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004413 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004414 if (done == angles.count()) {
4415 chase.pop(&span);
4416 continue;
4417 }
4418 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004419 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004420#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004421 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004422#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004423 if (!sortable) {
4424 chase.pop(&span);
4425 continue;
4426 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004427 // find first angle, initialize winding to computed fWindSum
4428 int firstIndex = -1;
4429 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004430 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004431 do {
4432 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004433 segment = angle->segment();
4434 winding = segment->windSum(angle);
4435 } while (winding == SK_MinS32);
4436 int spanWinding = segment->spanSign(angle->start(), angle->end());
4437 #if DEBUG_WINDING
4438 SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
4439 __FUNCTION__, winding, spanWinding, contourWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00004440 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004441 // turn swinding into contourWinding
4442 if (spanWinding * winding < 0) {
4443 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004444 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004445 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004446 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004447 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004448 // we care about first sign and whether wind sum indicates this
4449 // edge is inside or outside. Maybe need to pass span winding
4450 // or first winding or something into this function?
4451 // advance to first undone angle, then return it and winding
4452 // (to set whether edges are active or not)
4453 int nextIndex = firstIndex + 1;
4454 int angleCount = sorted.count();
4455 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004456 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00004457 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00004458 do {
4459 SkASSERT(nextIndex != firstIndex);
4460 if (nextIndex == angleCount) {
4461 nextIndex = 0;
4462 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004463 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00004464 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004465 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00004466 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004467 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00004468 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
4469 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004470 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004471 tIndex = angle->start();
4472 endIndex = angle->end();
4473 int lesser = SkMin32(tIndex, endIndex);
4474 const Span& nextSpan = segment->span(lesser);
4475 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004476#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00004477 // FIXME: this be wrong. assign startWinding if edge is in
4478 // same direction. If the direction is opposite, winding to
4479 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00004480 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004481 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004482 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004483 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004484#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004485 break;
4486 }
4487 } while (++nextIndex != lastIndex);
4488 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004489 }
4490 return NULL;
4491}
4492
caryclark@google.com027de222012-07-12 12:52:50 +00004493#if DEBUG_ACTIVE_SPANS
4494static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004495 int index;
4496 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00004497 contourList[index]->debugShowActiveSpans();
4498 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004499 for (index = 0; index < contourList.count(); ++ index) {
4500 contourList[index]->validateActiveSpans();
4501 }
caryclark@google.com027de222012-07-12 12:52:50 +00004502}
4503#endif
4504
caryclark@google.com27c449a2012-07-27 18:26:38 +00004505static bool windingIsActive(int winding, int spanWinding) {
4506 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
4507 && (!winding || !spanWinding || winding == -spanWinding);
4508}
4509
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004510#if !SORTABLE_CONTOURS
4511static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
4512 int& endIndex) {
4513 Segment* result;
4514 do {
4515 int contourCount = contourList.count();
4516 int cIndex = 0;
4517 Segment* topStart;
4518 SkScalar bestY = SK_ScalarMax;
4519 Contour* contour;
4520 do {
4521 contour = contourList[cIndex];
4522 topStart = contour->topSortableSegment(bestY);
4523 } while (!topStart && ++cIndex < contourCount);
4524 if (!topStart) {
4525 return NULL;
4526 }
4527 while (++cIndex < contourCount) {
4528 contour = contourList[cIndex];
4529 if (bestY < contour->bounds().fTop) {
4530 continue;
4531 }
4532 SkScalar testY = SK_ScalarMax;
4533 Segment* test = contour->topSortableSegment(testY);
4534 if (!test || bestY <= testY) {
4535 continue;
4536 }
4537 topStart = test;
4538 bestY = testY;
4539 }
4540 result = topStart->findTop(index, endIndex);
4541 } while (!result);
4542 return result;
4543}
4544#endif
4545
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004546// Each segment may have an inside or an outside. Segments contained within
4547// winding may have insides on either side, and form a contour that should be
4548// ignored. Segments that are coincident with opposing direction segments may
4549// have outsides on either side, and should also disappear.
4550// 'Normal' segments will have one inside and one outside. Subsequent connections
4551// when winding should follow the intersection direction. If more than one edge
4552// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004553 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00004554 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004555// returns true if all edges were processed
4556static bool bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004557 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004558 bool unsortable = false;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004559 do {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004560#if SORTABLE_CONTOURS // old way
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004561 Segment* topStart = findTopContour(contourList);
caryclark@google.com15fa1382012-05-07 20:49:36 +00004562 if (!topStart) {
4563 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00004564 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004565 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00004566 // follow edges to intersection by changing the index by direction.
4567 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004568 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004569#else // new way: iterate while top is unsortable
4570 int index, endIndex;
4571 Segment* current = findSortableTop(contourList, index, endIndex);
4572 if (!current) {
4573 break;
4574 }
4575#endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004576 int contourWinding;
4577 if (firstContour) {
4578 contourWinding = 0;
4579 firstContour = false;
4580 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004581 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004582 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00004583 if (sumWinding == SK_MinS32) {
4584 sumWinding = current->computeSum(index, endIndex);
4585 }
4586 if (sumWinding == SK_MinS32) {
4587 contourWinding = innerContourCheck(contourList, current,
4588 index, endIndex);
4589 } else {
4590 contourWinding = sumWinding;
4591 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004592 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
4593 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00004594 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004595 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004596#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004597 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004598 sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00004599 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004600#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004601 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004602#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004603 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004604 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
4605#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004606 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004607 SkPoint lastPt;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004608 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004609 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004610 // FIXME: needs work. While it works in limited situations, it does
4611 // not always compute winding correctly. Active should be removed and instead
4612 // the initial winding should be correctly passed in so that if the
4613 // inner contour is wound the same way, it never finds an accumulated
4614 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00004615 // other than zero when resolving sorted angles.
caryclark@google.com27c449a2012-07-27 18:26:38 +00004616 bool active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004617 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004618 do {
caryclark@google.com0e08a192012-07-13 21:07:52 +00004619 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00004620 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
4621 __FUNCTION__, active ? "true" : "false",
4622 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004623 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004624 const SkPoint* firstPt = NULL;
4625 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004626 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004627 int nextStart = index;
4628 int nextEnd = endIndex;
4629 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004630 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004631 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00004632 if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
4633 lastPt = current->addCurveTo(index, endIndex, simple, true);
4634 SkASSERT(*firstPt == lastPt);
4635 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004636 break;
4637 }
4638 if (!firstPt) {
4639 firstPt = &current->addMoveTo(index, simple, active);
4640 }
4641 lastPt = current->addCurveTo(index, endIndex, simple, active);
4642 current = next;
4643 index = nextStart;
4644 endIndex = nextEnd;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004645 } while (*firstPt != lastPt && (active || !current->done()));
4646 if (firstPt && active) {
4647 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004648 SkDebugf("path.close();\n");
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004649 #endif
4650 simple.close();
4651 }
caryclark@google.come21cb182012-07-23 21:26:31 +00004652 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004653 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00004654 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004655 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004656 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00004657 break;
4658 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004659 int lesser = SkMin32(index, endIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004660 spanWinding = current->spanSign(index, endIndex);
4661 winding = current->windSum(lesser);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004662 bool inner = useInnerWinding(winding - spanWinding, winding);
4663 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004664 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
caryclark@google.com59823f72012-08-09 18:17:47 +00004665 " inner=%d result=%d\n",
caryclark@google.com2ddff932012-08-07 21:25:27 +00004666 __FUNCTION__, current->debugID(), current->t(lesser),
4667 spanWinding, winding, SkSign32(index - endIndex),
4668 useInnerWinding(winding - spanWinding, winding),
caryclark@google.com2ddff932012-08-07 21:25:27 +00004669 inner ? winding - spanWinding : winding);
4670 #endif
4671 if (inner) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004672 winding -= spanWinding;
4673 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004674 active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004675 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004676 } while (true);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004677 return !unsortable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004678}
4679
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004680// returns true if all edges were processed
4681static bool bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00004682 Segment* current;
4683 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004684 bool unsortable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00004685 while ((current = findUndone(contourList, start, end))) {
4686 const SkPoint* firstPt = NULL;
4687 SkPoint lastPt;
4688 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004689 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004690 int nextStart = start;
4691 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004692 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00004693 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00004694 if (firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
4695 lastPt = current->addCurveTo(start, end, simple, true);
4696 SkASSERT(*firstPt == lastPt);
4697 }
caryclark@google.com24bec792012-08-20 12:43:57 +00004698 break;
4699 }
4700 if (!firstPt) {
4701 firstPt = &current->addMoveTo(start, simple, true);
4702 }
4703 lastPt = current->addCurveTo(start, end, simple, true);
4704 current = next;
4705 start = nextStart;
4706 end = nextEnd;
4707 } while (*firstPt != lastPt);
4708 if (firstPt) {
4709 #if DEBUG_PATH_CONSTRUCTION
4710 SkDebugf("%s close\n", __FUNCTION__);
4711 #endif
4712 simple.close();
4713 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004714 #if DEBUG_ACTIVE_SPANS
4715 debugShowActiveSpans(contourList);
4716 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00004717 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004718 return !unsortable;
caryclark@google.com24bec792012-08-20 12:43:57 +00004719}
4720
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004721static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
4722 int contourCount = contourList.count();
4723 for (int cTest = 0; cTest < contourCount; ++cTest) {
4724 Contour* contour = contourList[cTest];
4725 contour->fixOtherTIndex();
4726 }
4727}
4728
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004729#if !SORTABLE_CONTOURS
4730static void sortSegments(SkTDArray<Contour*>& contourList) {
4731 int contourCount = contourList.count();
4732 for (int cTest = 0; cTest < contourCount; ++cTest) {
4733 Contour* contour = contourList[cTest];
4734 contour->sortSegments();
4735 }
4736}
4737#endif
4738
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004739static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004740 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004741 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004742 if (count == 0) {
4743 return;
4744 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004745 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004746 *list.append() = &contours[index];
4747 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004748 QSort<Contour>(list.begin(), list.end() - 1);
4749}
4750
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004751static void assemble(SkPath& simple) {
4752 // TODO: find the non-closed paths and connect them together
4753 SkASSERT(0);
4754}
4755
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004756void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004757 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004758 simple.reset();
4759 simple.setFillType(SkPath::kEvenOdd_FillType);
4760
4761 // turn path into list of segments
4762 SkTArray<Contour> contours;
4763 // FIXME: add self-intersecting cubics' T values to segment
4764 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004765 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004766 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004767 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004768 Contour** currentPtr = contourList.begin();
4769 if (!currentPtr) {
4770 return;
4771 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004772 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004773 // find all intersections between segments
4774 do {
4775 Contour** nextPtr = currentPtr;
4776 Contour* current = *currentPtr++;
4777 Contour* next;
4778 do {
4779 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004780 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004781 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004782 // eat through coincident edges
caryclark@google.com235f56a2012-09-14 14:19:30 +00004783 coincidenceCheck(contourList);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004784 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004785#if !SORTABLE_CONTOURS
4786 sortSegments(contourList);
4787#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004788 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004789 if (builder.xorMask() == kWinding_Mask
4790 ? !bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00004791 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004792 { // if some edges could not be resolved, assemble remaining fragments
4793 assemble(simple);
caryclark@google.com24bec792012-08-20 12:43:57 +00004794 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004795}
4796