blob: 80c356602868e603f8149fbc93ff7560bc867b1f [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.comf839c032012-10-26 21:03:50 +000028#define PIN_ADD_T 0
caryclark@google.com0b7da432012-10-31 19:00:20 +000029#define TRY_ROTATE 1
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.com31143cf2012-11-09 22:14:19 +000032#define FORCE_RELEASE 1 // set force release to 1 for multiple thread -- no debugging
caryclark@google.comfa0588f2012-04-26 21:01:06 +000033
caryclark@google.com31143cf2012-11-09 22:14:19 +000034#if FORCE_RELEASE || defined SK_RELEASE
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.comf839c032012-10-26 21:03:50 +000045#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com47580692012-07-23 12:14:49 +000046#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000047#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000048#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000049
50#else
51
caryclark@google.com47580692012-07-23 12:14:49 +000052const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000053
caryclark@google.comc91dfe42012-10-16 12:06:27 +000054#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000055#define DEBUG_ADD_INTERSECTING_TS 1
56#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000057#define DEBUG_ANGLE 1
58#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000059#define DEBUG_CROSS 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000060#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000061#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000062#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000063#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000064#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000065
66#endif
67
caryclark@google.com6aea33f2012-10-09 14:11:58 +000068#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000069
caryclark@google.comfa0588f2012-04-26 21:01:06 +000070#if DEBUG_DUMP
71static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000072// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000073static int gContourID;
74static int gSegmentID;
75#endif
76
caryclark@google.com8dcf1142012-07-02 20:27:02 +000077#ifndef DEBUG_TEST
78#define DEBUG_TEST 0
79#endif
80
caryclark@google.com32546db2012-08-31 20:55:07 +000081#define MAKE_CONST_LINE(line, pts) \
82 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
83#define MAKE_CONST_QUAD(quad, pts) \
84 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
85 {pts[2].fX, pts[2].fY}}
86#define MAKE_CONST_CUBIC(cubic, pts) \
87 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
88 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
89
caryclark@google.comfa0588f2012-04-26 21:01:06 +000090static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
91 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000092 MAKE_CONST_LINE(aLine, a);
93 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +000094 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
95}
96
97static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
98 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000099 MAKE_CONST_QUAD(aQuad, a);
100 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000101 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000102}
103
caryclark@google.com32546db2012-08-31 20:55:07 +0000104static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000105 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000106 MAKE_CONST_CUBIC(aCubic, a);
107 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000108 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
109}
110
111static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
112 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000113 MAKE_CONST_QUAD(aQuad, a);
114 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000115#define TRY_QUARTIC_SOLUTION 1
116#if TRY_QUARTIC_SOLUTION
117 intersect2(aQuad, bQuad, intersections);
118#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000119 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000120#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000121 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000122}
123
124static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
125 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000126 MAKE_CONST_CUBIC(aCubic, a);
127 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000128 intersect(aCubic, bCubic, intersections);
129 return intersections.fUsed;
130}
131
132static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
133 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000134 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000135 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
136}
137
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000138static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
139 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000140 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000141 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
142}
143
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000144static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
145 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000146 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000147 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
148}
149
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000150static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
151 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000152 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000153 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
154}
155
156static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
157 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000158 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000159 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
160}
161
162static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
163 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000164 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000165 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000166}
167
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000168static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
169 SkScalar , SkScalar , bool , Intersections& ) = {
170 NULL,
171 VLineIntersect,
172 VQuadIntersect,
173 VCubicIntersect
174};
175
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000176static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000177 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000178 double x, y;
179 xy_at_t(line, t, x, y);
180 out->fX = SkDoubleToScalar(x);
181 out->fY = SkDoubleToScalar(y);
182}
183
184static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000185 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000186 double x, y;
187 xy_at_t(quad, t, x, y);
188 out->fX = SkDoubleToScalar(x);
189 out->fY = SkDoubleToScalar(y);
190}
191
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000192static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
193 MAKE_CONST_QUAD(quad, a);
194 xy_at_t(quad, t, out->x, out->y);
195}
196
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000197static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000198 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000199 double x, y;
200 xy_at_t(cubic, t, x, y);
201 out->fX = SkDoubleToScalar(x);
202 out->fY = SkDoubleToScalar(y);
203}
204
205static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
206 NULL,
207 LineXYAtT,
208 QuadXYAtT,
209 CubicXYAtT
210};
211
212static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000213 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000214 double x;
215 xy_at_t(aLine, t, x, *(double*) 0);
216 return SkDoubleToScalar(x);
217}
218
219static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000220 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000221 double x;
222 xy_at_t(quad, t, x, *(double*) 0);
223 return SkDoubleToScalar(x);
224}
225
226static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000227 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000228 double x;
229 xy_at_t(cubic, t, x, *(double*) 0);
230 return SkDoubleToScalar(x);
231}
232
233static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
234 NULL,
235 LineXAtT,
236 QuadXAtT,
237 CubicXAtT
238};
239
240static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000241 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000242 double y;
243 xy_at_t(aLine, t, *(double*) 0, y);
244 return SkDoubleToScalar(y);
245}
246
247static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000248 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000249 double y;
250 xy_at_t(quad, t, *(double*) 0, y);
251 return SkDoubleToScalar(y);
252}
253
254static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000255 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000256 double y;
257 xy_at_t(cubic, t, *(double*) 0, y);
258 return SkDoubleToScalar(y);
259}
260
261static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
262 NULL,
263 LineYAtT,
264 QuadYAtT,
265 CubicYAtT
266};
267
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000268static SkScalar LineDXAtT(const SkPoint a[2], double ) {
269 return a[1].fX - a[0].fX;
270}
271
272static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000273 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000274 double x;
275 dxdy_at_t(quad, t, x, *(double*) 0);
276 return SkDoubleToScalar(x);
277}
278
279static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000280 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000281 double x;
282 dxdy_at_t(cubic, t, x, *(double*) 0);
283 return SkDoubleToScalar(x);
284}
285
286static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
287 NULL,
288 LineDXAtT,
289 QuadDXAtT,
290 CubicDXAtT
291};
292
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000293static void LineSubDivide(const SkPoint a[2], double startT, double endT,
294 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000295 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000296 _Line dst;
297 sub_divide(aLine, startT, endT, dst);
298 sub[0].fX = SkDoubleToScalar(dst[0].x);
299 sub[0].fY = SkDoubleToScalar(dst[0].y);
300 sub[1].fX = SkDoubleToScalar(dst[1].x);
301 sub[1].fY = SkDoubleToScalar(dst[1].y);
302}
303
304static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
305 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000306 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000307 Quadratic dst;
308 sub_divide(aQuad, startT, endT, dst);
309 sub[0].fX = SkDoubleToScalar(dst[0].x);
310 sub[0].fY = SkDoubleToScalar(dst[0].y);
311 sub[1].fX = SkDoubleToScalar(dst[1].x);
312 sub[1].fY = SkDoubleToScalar(dst[1].y);
313 sub[2].fX = SkDoubleToScalar(dst[2].x);
314 sub[2].fY = SkDoubleToScalar(dst[2].y);
315}
316
317static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
318 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000319 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000320 Cubic dst;
321 sub_divide(aCubic, startT, endT, dst);
322 sub[0].fX = SkDoubleToScalar(dst[0].x);
323 sub[0].fY = SkDoubleToScalar(dst[0].y);
324 sub[1].fX = SkDoubleToScalar(dst[1].x);
325 sub[1].fY = SkDoubleToScalar(dst[1].y);
326 sub[2].fX = SkDoubleToScalar(dst[2].x);
327 sub[2].fY = SkDoubleToScalar(dst[2].y);
328 sub[3].fX = SkDoubleToScalar(dst[3].x);
329 sub[3].fY = SkDoubleToScalar(dst[3].y);
330}
331
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000332static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
333 SkPoint []) = {
334 NULL,
335 LineSubDivide,
336 QuadSubDivide,
337 CubicSubDivide
338};
339
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000340static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000341 _Line sub) {
342 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000343 _Line dst;
344 sub_divide(aLine, startT, endT, dst);
345 sub[0] = dst[0];
346 sub[1] = dst[1];
347}
348
349static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000350 Quadratic sub) {
351 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000352 Quadratic dst;
353 sub_divide(aQuad, startT, endT, dst);
354 sub[0] = dst[0];
355 sub[1] = dst[1];
356 sub[2] = dst[2];
357}
358
359static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000360 Cubic sub) {
361 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000362 Cubic dst;
363 sub_divide(aCubic, startT, endT, dst);
364 sub[0] = dst[0];
365 sub[1] = dst[1];
366 sub[2] = dst[2];
367 sub[3] = dst[3];
368}
369
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000370#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000371static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
372 SkRect& bounds) {
373 SkPoint dst[3];
374 QuadSubDivide(a, startT, endT, dst);
375 bounds.fLeft = bounds.fRight = dst[0].fX;
376 bounds.fTop = bounds.fBottom = dst[0].fY;
377 for (int index = 1; index < 3; ++index) {
378 bounds.growToInclude(dst[index].fX, dst[index].fY);
379 }
380}
381
382static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
383 SkRect& bounds) {
384 SkPoint dst[4];
385 CubicSubDivide(a, startT, endT, dst);
386 bounds.fLeft = bounds.fRight = dst[0].fX;
387 bounds.fTop = bounds.fBottom = dst[0].fY;
388 for (int index = 1; index < 4; ++index) {
389 bounds.growToInclude(dst[index].fX, dst[index].fY);
390 }
391}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000392#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000393
caryclark@google.com15fa1382012-05-07 20:49:36 +0000394static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000395 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000396 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000397 Quadratic dst;
398 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000399 if (order == 2) { // quad became line
400 for (int index = 0; index < order; ++index) {
401 SkPoint* pt = reducePts.append();
402 pt->fX = SkDoubleToScalar(dst[index].x);
403 pt->fY = SkDoubleToScalar(dst[index].y);
404 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000405 }
406 return (SkPath::Verb) (order - 1);
407}
408
409static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
410 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000411 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000412 Cubic dst;
413 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000414 if (order == 2 || order == 3) { // cubic became line or quad
415 for (int index = 0; index < order; ++index) {
416 SkPoint* pt = reducePts.append();
417 pt->fX = SkDoubleToScalar(dst[index].x);
418 pt->fY = SkDoubleToScalar(dst[index].y);
419 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000420 }
421 return (SkPath::Verb) (order - 1);
422}
423
caryclark@google.com15fa1382012-05-07 20:49:36 +0000424static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000425 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000426 return isLinear(aQuad, 0, 2);
427}
428
429static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000430 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000431 return isLinear(aCubic, 0, 3);
432}
433
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000434static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000435 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000436 double x[2];
437 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000438 xy_at_t(aLine, endT, x[1], *(double*) 0);
439 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000440}
441
442static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000443 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000444 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000445}
446
447static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000448 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000449 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000450}
451
452static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
453 NULL,
454 LineLeftMost,
455 QuadLeftMost,
456 CubicLeftMost
457};
458
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000459#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000460static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
461 Intersections& intersections) {
462 MAKE_CONST_QUAD(aQuad, a);
463 MAKE_CONST_LINE(bLine, b);
464 return intersectRay(aQuad, bLine, intersections);
465}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000466#endif
467
468static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
469 Intersections& intersections) {
470 MAKE_CONST_QUAD(aQuad, a);
471 return intersectRay(aQuad, bLine, intersections);
472}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000473
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000474class Segment;
475
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000476struct Span {
477 Segment* fOther;
478 mutable SkPoint fPt; // lazily computed as needed
479 double fT;
480 double fOtherT; // value at fOther[fOtherIndex].fT
481 int fOtherIndex; // can't be used during intersection
caryclark@google.com31143cf2012-11-09 22:14:19 +0000482 int fWindSum; // accumulated from contours surrounding this one.
483 int fOppSum; // for binary operators: the opposite winding sum
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000484 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000485 int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000486 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000487 bool fUnsortableStart; // set when start is part of an unsortable pair
488 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.comf839c032012-10-26 21:03:50 +0000489 bool fTiny; // if set, span may still be considered once for edge following
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000490};
491
caryclark@google.com15fa1382012-05-07 20:49:36 +0000492// sorting angles
493// given angles of {dx dy ddx ddy dddx dddy} sort them
494class Angle {
495public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000496 // FIXME: this is bogus for quads and cubics
497 // if the quads and cubics' line from end pt to ctrl pt are coincident,
498 // there's no obvious way to determine the curve ordering from the
499 // derivatives alone. In particular, if one quadratic's coincident tangent
500 // is longer than the other curve, the final control point can place the
501 // longer curve on either side of the shorter one.
502 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
503 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000504
caryclark@google.com32546db2012-08-31 20:55:07 +0000505 /*(
506 for quads and cubics, set up a parameterized line (e.g. LineParameters )
507 for points [0] to [1]. See if point [2] is on that line, or on one side
508 or the other. If it both quads' end points are on the same side, choose
509 the shorter tangent. If the tangents are equal, choose the better second
510 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000511
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000512 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000513 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000514 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000515 double y = dy();
516 double ry = rh.dy();
517 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
518 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000519 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000520 double x = dx();
521 double rx = rh.dx();
522 if (y == 0 && ry == 0 && x * rx < 0) {
523 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000524 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000525 double x_ry = x * ry;
526 double rx_y = rx * y;
527 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000528 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000529 return cmp < 0;
530 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000531 if (approximately_zero(x_ry) && approximately_zero(rx_y)
532 && !approximately_zero_squared(cmp)) {
533 return cmp < 0;
534 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000535 // at this point, the initial tangent line is coincident
caryclark@google.com31143cf2012-11-09 22:14:19 +0000536 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
537 || !approximately_zero(rh.fSide))) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000538 // FIXME: running demo will trigger this assertion
539 // (don't know if commenting out will trigger further assertion or not)
540 // commenting it out allows demo to run in release, though
541 // SkASSERT(fSide != rh.fSide);
542 return fSide < rh.fSide;
543 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000544 // see if either curve can be lengthened and try the tangent compare again
545 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
546 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
547 Angle longer = *this;
548 Angle rhLonger = rh;
549 if (longer.lengthen() | rhLonger.lengthen()) {
550 return longer < rhLonger;
551 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000552 // what if we extend in the other direction?
553 longer = *this;
554 rhLonger = rh;
555 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
556 return longer < rhLonger;
557 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000558 }
caryclark@google.com185c7c42012-10-19 18:26:24 +0000559 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
caryclark@google.com31143cf2012-11-09 22:14:19 +0000560 || (rh.fVerb == SkPath::kLine_Verb
561 && approximately_zero(rx) && approximately_zero(ry))) {
caryclark@google.com185c7c42012-10-19 18:26:24 +0000562 // See general unsortable comment below. This case can happen when
563 // one line has a non-zero change in t but no change in x and y.
564 fUnsortable = true;
565 rh.fUnsortable = true;
566 return this < &rh; // even with no solution, return a stable sort
567 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000568 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
569 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000570 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000571 // end of the shorter tangent to midway between the end points
572 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000573 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
574 double len = fTangent1.normalSquared();
575 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000576 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000577 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000578 int roots, rroots;
579 bool flip = false;
580 do {
581 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
582 double midX = (q[0].x + q[2].x) / 2;
583 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000584 ray[0] = q[1];
585 ray[1].x = midX;
586 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000587 SkASSERT(ray[0] != ray[1]);
588 roots = QuadRayIntersect(fPts, ray, i);
589 rroots = QuadRayIntersect(rh.fPts, ray, ri);
590 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000591 if (roots == 0 || rroots == 0) {
592 // FIXME: we don't have a solution in this case. The interim solution
593 // is to mark the edges as unsortable, exclude them from this and
594 // future computations, and allow the returned path to be fragmented
595 fUnsortable = true;
596 rh.fUnsortable = true;
597 return this < &rh; // even with no solution, return a stable sort
598 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000599 _Point loc;
600 double best = SK_ScalarInfinity;
601 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000602 int index;
603 for (index = 0; index < roots; ++index) {
604 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000605 dx = loc.x - ray[0].x;
606 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000607 dist = dx * dx + dy * dy;
608 if (best > dist) {
609 best = dist;
610 }
611 }
612 for (index = 0; index < rroots; ++index) {
613 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000614 dx = loc.x - ray[0].x;
615 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000616 dist = dx * dx + dy * dy;
617 if (best > dist) {
618 return fSide < 0;
619 }
620 }
621 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000622 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000623
caryclark@google.com47580692012-07-23 12:14:49 +0000624 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000625 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000626 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000627
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000628 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000629 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000630 }
631
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000632 int end() const {
633 return fEnd;
634 }
635
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000636 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000637 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000638 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000639
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000640 bool lengthen() {
641 int newEnd = fEnd;
642 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
643 fEnd = newEnd;
644 setSpans();
645 return true;
646 }
647 return false;
648 }
649
caryclark@google.coma461ff02012-10-11 12:54:23 +0000650 bool reverseLengthen() {
651 if (fReversed) {
652 return false;
653 }
654 int newEnd = fStart;
655 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
656 fEnd = newEnd;
657 fReversed = true;
658 setSpans();
659 return true;
660 }
661 return false;
662 }
663
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000664 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000665 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000666 fSegment = segment;
667 fStart = start;
668 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000669 fPts = orig;
670 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000671 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000672 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000673 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000674 setSpans();
675 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000676
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000677 void setSpans() {
678 double startT = (*fSpans)[fStart].fT;
679 double endT = (*fSpans)[fEnd].fT;
680 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000681 case SkPath::kLine_Verb:
682 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000683 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000684 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
685 fTangent1.lineEndPoints(l);
caryclark@google.comf839c032012-10-26 21:03:50 +0000686 fUnsortable = dx() == 0 && dy() == 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000687 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000688 break;
689 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000690 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000691 fTangent1.quadEndPoints(fQ, 0, 1);
692 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000693 break;
694 case SkPath::kCubic_Verb:
695 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000696 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000697 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000698 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000699 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000700 default:
701 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000702 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000703 if (fUnsortable) {
704 return;
705 }
706 SkASSERT(fStart != fEnd);
707 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
708 for (int index = fStart; index != fEnd; index += step) {
709 if ((*fSpans)[index].fUnsortableStart) {
710 fUnsortable = true;
711 return;
712 }
713 if (index != fStart && (*fSpans)[index].fUnsortableEnd) {
714 fUnsortable = true;
715 return;
716 }
717 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000718 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000719
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000720 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000721 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000722 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000723
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000724 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000725 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000726 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000727
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000728 const SkTDArray<Span>* spans() const {
729 return fSpans;
730 }
731
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000732 int start() const {
733 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000734 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000735
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000736 bool unsortable() const {
737 return fUnsortable;
738 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000739
caryclark@google.comc899ad92012-08-23 15:24:42 +0000740#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000741 const SkPoint* pts() const {
742 return fPts;
743 }
744
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000745 SkPath::Verb verb() const {
746 return fVerb;
747 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000748
caryclark@google.comc899ad92012-08-23 15:24:42 +0000749 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000750 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000751 }
752#endif
753
caryclark@google.com15fa1382012-05-07 20:49:36 +0000754private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000755 const SkPoint* fPts;
756 Quadratic fQ;
757 SkPath::Verb fVerb;
758 double fSide;
759 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000760 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000761 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000762 int fStart;
763 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000764 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000765 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000766};
767
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000768// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000769struct Bounds : public SkRect {
770 static bool Intersects(const Bounds& a, const Bounds& b) {
771 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
772 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
773 }
774
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000775 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
776 if (left < fLeft) {
777 fLeft = left;
778 }
779 if (top < fTop) {
780 fTop = top;
781 }
782 if (right > fRight) {
783 fRight = right;
784 }
785 if (bottom > fBottom) {
786 fBottom = bottom;
787 }
788 }
789
790 void add(const Bounds& toAdd) {
791 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
792 }
793
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000794 bool isEmpty() {
795 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000796 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000797 || isnan(fLeft) || isnan(fRight)
798 || isnan(fTop) || isnan(fBottom);
799 }
800
801 void setCubicBounds(const SkPoint a[4]) {
802 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000803 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000804 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000805 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
806 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000807 }
808
809 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000810 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000811 _Rect dRect;
812 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000813 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
814 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000815 }
816};
817
caryclark@google.com2ddff932012-08-07 21:25:27 +0000818static bool useInnerWinding(int outerWinding, int innerWinding) {
819 SkASSERT(outerWinding != innerWinding);
820 int absOut = abs(outerWinding);
821 int absIn = abs(innerWinding);
822 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
823 if (outerWinding * innerWinding < 0) {
824#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000825 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000826 outerWinding, innerWinding, result ? "true" : "false");
827#endif
828 }
829 return result;
830}
831
caryclark@google.com31143cf2012-11-09 22:14:19 +0000832static const bool gOpLookup[][2][2] = {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000833 // ==0 !=0
834 // b a b a
835 {{true , false}, {false, true }}, // a - b
836 {{false, false}, {true , true }}, // a & b
837 {{true , true }, {false, false}}, // a | b
838 {{true , true }, {true , true }}, // a ^ b
839};
840
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000841static bool activeOp(bool angleIsOp, int otherNonZero, int otherCoin, ShapeOp op) {
842 if (otherNonZero != otherCoin) {
843 return op == kIntersect_Op || op == kUnion_Op;
844 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000845 return gOpLookup[op][otherNonZero][angleIsOp];
caryclark@google.com235f56a2012-09-14 14:19:30 +0000846}
847
caryclark@google.comf839c032012-10-26 21:03:50 +0000848// wrap path to keep track of whether the contour is initialized and non-empty
849class PathWrapper {
850public:
851 PathWrapper(SkPath& path)
852 : fPathPtr(&path)
853 {
854 init();
855 }
856
857 void close() {
858 if (!fHasMove) {
859 return;
860 }
861 bool callClose = isClosed();
862 lineTo();
863 if (fEmpty) {
864 return;
865 }
866 if (callClose) {
867 #if DEBUG_PATH_CONSTRUCTION
868 SkDebugf("path.close();\n");
869 #endif
870 fPathPtr->close();
871 }
872 init();
873 }
874
875 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
876 lineTo();
877 moveTo();
878#if DEBUG_PATH_CONSTRUCTION
879 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
880 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
881#endif
882 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
883 fDefer[0] = fDefer[1] = pt3;
884 fEmpty = false;
885 }
886
887 void deferredLine(const SkPoint& pt) {
888 if (pt == fDefer[1]) {
889 return;
890 }
891 if (changedSlopes(pt)) {
892 lineTo();
893 fDefer[0] = fDefer[1];
894 }
895 fDefer[1] = pt;
896 }
897
898 void deferredMove(const SkPoint& pt) {
899 fMoved = true;
900 fHasMove = true;
901 fEmpty = true;
902 fDefer[0] = fDefer[1] = pt;
903 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000904
caryclark@google.comf839c032012-10-26 21:03:50 +0000905 void deferredMoveLine(const SkPoint& pt) {
906 if (!fHasMove) {
907 deferredMove(pt);
908 }
909 deferredLine(pt);
910 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000911
caryclark@google.comf839c032012-10-26 21:03:50 +0000912 bool hasMove() const {
913 return fHasMove;
914 }
915
916 void init() {
917 fEmpty = true;
918 fHasMove = false;
919 fMoved = false;
920 }
921
922 bool isClosed() const {
923 return !fEmpty && fFirstPt == fDefer[1];
924 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000925
caryclark@google.comf839c032012-10-26 21:03:50 +0000926 void lineTo() {
927 if (fDefer[0] == fDefer[1]) {
928 return;
929 }
930 moveTo();
931 fEmpty = false;
932#if DEBUG_PATH_CONSTRUCTION
933 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
934#endif
935 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
936 fDefer[0] = fDefer[1];
937 }
938
939 const SkPath* nativePath() const {
940 return fPathPtr;
941 }
942
943 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
944 lineTo();
945 moveTo();
946#if DEBUG_PATH_CONSTRUCTION
947 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
948 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
949#endif
950 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
951 fDefer[0] = fDefer[1] = pt2;
952 fEmpty = false;
953 }
954
955protected:
956 bool changedSlopes(const SkPoint& pt) const {
957 if (fDefer[0] == fDefer[1]) {
958 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000959 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000960 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
961 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
962 SkScalar lineDx = pt.fX - fDefer[1].fX;
963 SkScalar lineDy = pt.fY - fDefer[1].fY;
964 return deferDx * lineDy != deferDy * lineDx;
965 }
966
967 void moveTo() {
968 if (!fMoved) {
969 return;
970 }
971 fFirstPt = fDefer[0];
972#if DEBUG_PATH_CONSTRUCTION
973 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
974#endif
975 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
976 fMoved = false;
977 }
978
979private:
980 SkPath* fPathPtr;
981 SkPoint fDefer[2];
982 SkPoint fFirstPt;
983 bool fEmpty;
984 bool fHasMove;
985 bool fMoved;
986};
987
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000988class Segment {
989public:
990 Segment() {
991#if DEBUG_DUMP
992 fID = ++gSegmentID;
993#endif
994 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +0000995
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000996 bool operator<(const Segment& rh) const {
997 return fBounds.fTop < rh.fBounds.fTop;
998 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000999
caryclark@google.com9764cc62012-07-12 19:29:45 +00001000 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
1001 if (activeAngleInner(index, done, angles)) {
1002 return true;
1003 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001004 double referenceT = fTs[index].fT;
1005 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001006 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001007 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001008 return true;
1009 }
1010 }
1011 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001012 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001013 return true;
1014 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001015 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001016 return false;
1017 }
1018
caryclark@google.com9764cc62012-07-12 19:29:45 +00001019 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001020 Span* span = &fTs[index];
1021 Segment* other = span->fOther;
1022 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001023 return other->activeAngleInner(oIndex, done, angles);
1024 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001025
caryclark@google.com9764cc62012-07-12 19:29:45 +00001026 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001027 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001028 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001029 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001030 if (upSpan.fWindValue) {
1031 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001032 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001033 done++;
1034 } else if (upSpan.fWindSum != SK_MinS32) {
1035 return true;
1036 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001037 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001038 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001039 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001040 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001041 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001042 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001043 if (downSpan.fWindValue) {
1044 addAngle(angles, index, prev);
1045 if (downSpan.fDone) {
1046 done++;
1047 } else if (downSpan.fWindSum != SK_MinS32) {
1048 return true;
1049 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001050 }
1051 }
1052 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001053 }
1054
caryclark@google.comf839c032012-10-26 21:03:50 +00001055 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001056 SkASSERT(!done());
1057 int count = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001058 result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001059 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001060 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001061 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001062 const Span& span = fTs[index];
1063 if (span.fUnsortableStart | lastUnsortable) {
1064 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001065 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001066 if (!span.fDone | !lastDone) {
1067 const SkPoint& xy = xyAtT(index);
1068 if (result.fY < xy.fY) {
1069 goto next;
1070 }
1071 if (result.fY == xy.fY && result.fX < xy.fX) {
1072 goto next;
1073 }
1074 result = xy;
1075 }
1076 next:
1077 lastDone = span.fDone;
1078 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001079 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001080 SkASSERT(result.fY < SK_ScalarMax);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001081 }
1082
1083 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001084 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001085 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001086#if DEBUG_ANGLE
caryclark@google.com31143cf2012-11-09 22:14:19 +00001087 if (angles.count() > 1 && !fTs[start].fTiny) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001088 SkPoint angle0Pt, newPt;
1089 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1090 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1091 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1092 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1093 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1094 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001095#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001096 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001097 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001098
caryclark@google.com2ddff932012-08-07 21:25:27 +00001099 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001100 double oEnd) {
1101 int tIndex = -1;
1102 int tCount = fTs.count();
1103 int oIndex = -1;
1104 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001105 do {
1106 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001107 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001108 int tIndexStart = tIndex;
1109 do {
1110 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001111 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001112 int oIndexStart = oIndex;
1113 double nextT;
1114 do {
1115 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001116 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001117 double oNextT;
1118 do {
1119 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001120 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001121 // at this point, spans before and after are at:
1122 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1123 // if tIndexStart == 0, no prior span
1124 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001125
caryclark@google.comcc905052012-07-25 20:59:42 +00001126 // advance the span with zero winding
1127 // if the following span exists (not past the end, non-zero winding)
1128 // connect the two edges
1129 if (!fTs[tIndexStart].fWindValue) {
1130 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1131 #if DEBUG_CONCIDENT
1132 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1133 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001134 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1135 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001136 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001137 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001138 }
1139 if (nextT < 1 && fTs[tIndex].fWindValue) {
1140 #if DEBUG_CONCIDENT
1141 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1142 __FUNCTION__, fID, other.fID, tIndex,
1143 fTs[tIndex].fT, xyAtT(tIndex).fX,
1144 xyAtT(tIndex).fY);
1145 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001146 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001147 }
1148 } else {
1149 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1150 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1151 #if DEBUG_CONCIDENT
1152 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1153 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001154 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1155 other.xyAtT(oIndexStart).fY);
1156 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001157 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001158 }
1159 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1160 #if DEBUG_CONCIDENT
1161 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1162 __FUNCTION__, fID, other.fID, oIndex,
1163 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1164 other.xyAtT(oIndex).fY);
1165 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1166 #endif
1167 }
1168 }
1169 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001170
caryclark@google.comcc905052012-07-25 20:59:42 +00001171 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1172 double oEnd) {
1173 // walk this to outsideTs[0]
1174 // walk other to outsideTs[1]
1175 // if either is > 0, add a pointer to the other, copying adjacent winding
1176 int tIndex = -1;
1177 int oIndex = -1;
1178 double tStart = outsideTs[0];
1179 double oStart = outsideTs[1];
1180 do {
1181 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001182 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001183 do {
1184 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001185 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001186 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001187 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001188 }
1189 tStart = fTs[tIndex].fT;
1190 oStart = other.fTs[oIndex].fT;
1191 do {
1192 double nextT;
1193 do {
1194 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001195 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001196 tStart = nextT;
1197 do {
1198 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001199 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001200 oStart = nextT;
1201 if (tStart == 1 && oStart == 1) {
1202 break;
1203 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001204 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001205 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001206 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001207
caryclark@google.com235f56a2012-09-14 14:19:30 +00001208 void addCubic(const SkPoint pts[4], bool operand) {
1209 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001210 fBounds.setCubicBounds(pts);
1211 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001212
caryclark@google.comf839c032012-10-26 21:03:50 +00001213 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001214 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001215 const SkPoint* ePtr;
1216 int lastT = fTs.count() - 1;
1217 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1218 ePtr = fPts;
1219 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001220 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001221 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1222 ePtr = edge;
1223 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001224 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001225 bool reverse = ePtr == fPts && start != 0;
1226 if (reverse) {
1227 path.deferredMoveLine(ePtr[fVerb]);
1228 switch (fVerb) {
1229 case SkPath::kLine_Verb:
1230 path.deferredLine(ePtr[0]);
1231 break;
1232 case SkPath::kQuad_Verb:
1233 path.quadTo(ePtr[1], ePtr[0]);
1234 break;
1235 case SkPath::kCubic_Verb:
1236 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1237 break;
1238 default:
1239 SkASSERT(0);
1240 }
1241 // return ePtr[0];
1242 } else {
1243 path.deferredMoveLine(ePtr[0]);
1244 switch (fVerb) {
1245 case SkPath::kLine_Verb:
1246 path.deferredLine(ePtr[1]);
1247 break;
1248 case SkPath::kQuad_Verb:
1249 path.quadTo(ePtr[1], ePtr[2]);
1250 break;
1251 case SkPath::kCubic_Verb:
1252 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1253 break;
1254 default:
1255 SkASSERT(0);
1256 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001257 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001258 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001259 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001260 }
1261
caryclark@google.com235f56a2012-09-14 14:19:30 +00001262 void addLine(const SkPoint pts[2], bool operand) {
1263 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001264 fBounds.set(pts, 2);
1265 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001266
caryclark@google.comf839c032012-10-26 21:03:50 +00001267#if 0
1268 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001269 const SkPoint& pt = xyAtT(tIndex);
1270 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001271 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001272 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001273 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001274 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001275#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001276
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001277 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001278 void addOtherT(int index, double otherT, int otherIndex) {
1279 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001280 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001281 if (precisely_less_than_zero(otherT)) {
1282 otherT = 0;
1283 } else if (precisely_greater_than_one(otherT)) {
1284 otherT = 1;
1285 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001286 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001287 span.fOtherT = otherT;
1288 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001289 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001290
caryclark@google.com235f56a2012-09-14 14:19:30 +00001291 void addQuad(const SkPoint pts[3], bool operand) {
1292 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001293 fBounds.setQuadBounds(pts);
1294 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001295
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001296 // Defer all coincident edge processing until
1297 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001298
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001299// no need to be tricky; insert in normal T order
1300// resolve overlapping ts when considering coincidence later
1301
1302 // add non-coincident intersection. Resulting edges are sorted in T.
1303 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001304 // FIXME: in the pathological case where there is a ton of intercepts,
1305 // binary search?
1306 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001307 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001308 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001309 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001310 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001311 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001312 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001313 newT = 1;
1314 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001315 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001316 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001317 // OPTIMIZATION: if there are three or more identical Ts, then
1318 // the fourth and following could be further insertion-sorted so
1319 // that all the edges are clockwise or counterclockwise.
1320 // This could later limit segment tests to the two adjacent
1321 // neighbors, although it doesn't help with determining which
1322 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001323 if (newT < fTs[index].fT) {
1324 insertedAt = index;
1325 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001326 }
1327 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001328 Span* span;
1329 if (insertedAt >= 0) {
1330 span = fTs.insert(insertedAt);
1331 } else {
1332 insertedAt = tCount;
1333 span = fTs.append();
1334 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001335 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001336 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001337 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001338 span->fWindSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001339 span->fOppSum = SK_MinS32;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001340 span->fWindValue = 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001341 span->fOppValue = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001342 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001343 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001344 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001345 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001346 span->fUnsortableStart = false;
1347 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001348 if (span - fTs.begin() > 0 && !span[-1].fDone
1349 && !precisely_negative(newT - span[-1].fT)
1350 // && approximately_negative(newT - span[-1].fT)
1351 && xyAtT(&span[-1]) == xyAtT(span)) {
1352 span[-1].fTiny = true;
1353 span[-1].fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001354 if (approximately_negative(newT - span[-1].fT)) {
1355 if (approximately_greater_than_one(newT)) {
1356 span[-1].fUnsortableStart = true;
1357 span[-2].fUnsortableEnd = true;
1358 }
1359 if (approximately_less_than_zero(span[-1].fT)) {
1360 span->fUnsortableStart = true;
1361 span[-1].fUnsortableEnd = true;
1362 }
1363 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001364 ++fDoneSpans;
1365 }
1366 if (fTs.end() - span > 1 && !span->fDone
1367 && !precisely_negative(span[1].fT - newT)
1368 // && approximately_negative(span[1].fT - newT)
1369 && xyAtT(&span[1]) == xyAtT(span)) {
1370 span->fTiny = true;
1371 span->fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001372 if (approximately_negative(span[1].fT - newT)) {
1373 if (approximately_greater_than_one(span[1].fT)) {
1374 span->fUnsortableStart = true;
1375 span[-1].fUnsortableEnd = true;
1376 }
1377 if (approximately_less_than_zero(newT)) {
1378 span[1].fUnsortableStart = true;
1379 span->fUnsortableEnd = true;
1380 }
1381 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001382 ++fDoneSpans;
1383 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001384 return insertedAt;
1385 }
1386
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001387 // set spans from start to end to decrement by one
1388 // note this walks other backwards
1389 // FIMXE: there's probably an edge case that can be constructed where
1390 // two span in one segment are separated by float epsilon on one span but
1391 // not the other, if one segment is very small. For this
1392 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001393 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001394 // sorted? It feels better in such a case to match the span's other span
1395 // pointer since both coincident segments must contain the same spans.
1396 void addTCancel(double startT, double endT, Segment& other,
1397 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001398 SkASSERT(!approximately_negative(endT - startT));
1399 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001400 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001401 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001402 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001403 ++index;
1404 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001405 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001406 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001407 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001408 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001409 Span* test = &fTs[index];
1410 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001411 SkTDArray<double> outsideTs;
1412 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001413 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001414 bool decrement = test->fWindValue && oTest->fWindValue && !binary;
caryclark@google.comcc905052012-07-25 20:59:42 +00001415 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001416 double testT = test->fT;
1417 double oTestT = oTest->fT;
1418 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001419 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001420 if (decrement) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001421 decrementSpan(span);
caryclark@google.com200c2112012-08-03 15:05:04 +00001422 } else if (track && span->fT < 1 && oTestT < 1) {
1423 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001424 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001425 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001426 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001427 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001428 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1429 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1430 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001431 while (approximately_negative(otherTMatchStart - oSpan->fT)
1432 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001433 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001434 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001435 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001436 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001437 other.decrementSpan(oSpan);
1438 } else if (track && oSpan->fT < 1 && testT < 1) {
1439 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001440 }
1441 if (!oIndex) {
1442 break;
1443 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001444 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001445 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001446 test = span;
1447 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001448 } while (!approximately_negative(endT - test->fT));
1449 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001450 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001451 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001452 double tStart = outsideTs[0];
1453 double oStart = outsideTs[1];
1454 addCancelOutsides(tStart, oStart, other, oEndT);
1455 int count = outsideTs.count();
1456 if (count > 2) {
1457 double tStart = outsideTs[count - 2];
1458 double oStart = outsideTs[count - 1];
1459 addCancelOutsides(tStart, oStart, other, oEndT);
1460 }
caryclark@google.com18063442012-07-25 12:05:18 +00001461 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001462 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001463 double tStart = oOutsideTs[0];
1464 double oStart = oOutsideTs[1];
1465 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001466 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001467 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001468
1469 int bumpCoincidentThis(const Span* oTest, const bool transfer, const bool decrementThis,
1470 const bool thisXor, const bool opp, int index, SkTDArray<double>& outsideTs,
1471 SkTDArray<double>& xOutsideTs)
1472 {
1473 Span* const test = &fTs[index];
1474 Span* end = test;
1475 const int startIndex = index;
1476 const double oStartT = oTest->fT;
1477 do {
1478 if (transfer) {
1479 if (!decrementThis & !thisXor & !opp) {
1480 #ifdef SK_DEBUG
1481 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
1482 #endif
1483 ++(end->fWindValue);
1484 } else if (decrementSpan(end)) {
1485 TrackOutside(outsideTs, end->fT, oStartT);
1486 }
1487 } else if (opp) {
1488 if (decrementThis) {
1489 if (decrementSpan(end)) {
1490 TrackOutside(outsideTs, end->fT, oStartT);
1491 }
1492 } else {
1493 end->fOppValue += oTest->fWindValue;
1494 }
1495 } else if (oTest->fWindValue) {
1496 SkASSERT(decrementThis);
1497 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1498 TrackOutside(xOutsideTs, end->fT, oStartT);
1499 }
1500 }
1501 end = &fTs[++index];
1502 } while (approximately_negative(end->fT - test->fT));
1503 return index;
1504 }
1505
1506 // because of the order in which coincidences are resolved, this and other
1507 // may not have the same intermediate points. Compute the corresponding
1508 // intermediate T values (using this as the master, other as the follower)
1509 // and walk other conditionally -- hoping that it catches up in the end
1510 int bumpCoincidentOther(const Span* test, const bool transfer, const bool decrementThis,
1511 const bool otherXor, const bool opp, const double tRatio, const double oEndT,
1512 int& oIndex, SkTDArray<double>& oOutsideTs)
1513 {
1514 Span* const oTest = &fTs[oIndex];
1515 Span* oEnd = oTest;
1516 const double startT = test->fT;
1517 const int oStartIndex = oIndex;
1518 const double oStartT = oTest->fT;
1519 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
1520 while (!approximately_negative(oEndT - oEnd->fT)
1521 && approximately_negative(oEnd->fT - otherTMatch)) {
1522 if (transfer) {
1523 if (decrementThis & !otherXor & !opp) {
1524 #ifdef SK_DEBUG
1525 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1526 #endif
1527 ++(oEnd->fWindValue);
1528 } else if (decrementSpan(oEnd)) {
1529 TrackOutside(oOutsideTs, oEnd->fT, startT);
1530 }
1531 } else if (opp) {
1532 if (decrementThis) {
1533 oEnd->fOppValue += test->fWindValue;
1534 } else {
1535 if (decrementSpan(oEnd)) {
1536 TrackOutside(oOutsideTs, oEnd->fT, startT);
1537 }
1538 }
1539 } else if (test->fWindValue) {
1540 SkASSERT(decrementThis);
1541 if (oStartIndex > 0 && fTs[oStartIndex - 1].fWindValue) {
1542 SkASSERT(0); // track for later?
1543 }
1544 }
1545 oEnd = &fTs[++oIndex];
1546 }
1547 return oIndex;
1548 }
1549
1550 // FIXME: need to test this case:
1551 // contourA has two segments that are coincident
1552 // contourB has two segments that are coincident in the same place
1553 // each ends up with +2/0 pairs for winding count
1554 // since logic below doesn't transfer count (only increments/decrements) can this be
1555 // resolved to +4/0 ?
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001556
1557 // set spans from start to end to increment the greater by one and decrement
1558 // the lesser
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001559 void addTCoincident(bool thisXor, bool otherXor, double startT, double endT,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001560 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001561 SkASSERT(!approximately_negative(endT - startT));
1562 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001563 bool opp = fOperand ^ other.fOperand;
1564 if (!opp) {
1565 otherXor = thisXor;
1566 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001567 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001568 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001569 ++index;
1570 }
1571 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001572 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001573 ++oIndex;
1574 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001575 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001576 Span* test = &fTs[index];
1577 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001578 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001579 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001580 SkTDArray<double> oOutsideTs;
1581 do {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001582 bool transfer = test->fWindValue && oTest->fWindValue && !opp;
1583 bool decrementThis = test->fWindValue < oTest->fWindValue;
1584 if (decrementThis) {
1585 oIndex = other.bumpCoincidentOther(test, transfer, decrementThis, otherXor, opp,
1586 tRatio, oEndT, oIndex, oOutsideTs);
1587 index = bumpCoincidentThis(oTest, transfer, decrementThis, thisXor, opp,
1588 index, outsideTs, xOutsideTs);
1589 } else {
1590 index = bumpCoincidentThis(oTest, transfer, decrementThis, thisXor, opp,
1591 index, outsideTs, xOutsideTs);
1592 oIndex = other.bumpCoincidentOther(test, transfer, decrementThis, otherXor, opp,
1593 tRatio, oEndT, oIndex, oOutsideTs);
caryclark@google.com59823f72012-08-09 18:17:47 +00001594 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001595 test = &fTs[index];
1596 oTest = &other.fTs[oIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001597 } while (!approximately_negative(endT - test->fT));
1598 SkASSERT(approximately_negative(oTest->fT - oEndT));
1599 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001600 if (!done()) {
1601 if (outsideTs.count()) {
1602 addCoinOutsides(outsideTs, other, oEndT);
1603 }
1604 if (xOutsideTs.count()) {
1605 addCoinOutsides(xOutsideTs, other, oEndT);
1606 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001607 }
1608 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001609 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001610 }
1611 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001612
caryclark@google.comcc905052012-07-25 20:59:42 +00001613 // FIXME: this doesn't prevent the same span from being added twice
1614 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001615 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001616 int tCount = fTs.count();
1617 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1618 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001619 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001620 break;
1621 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001622 if (approximately_negative(span.fT - t) && span.fOther == &other
1623 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001624#if DEBUG_ADD_T_PAIR
1625 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1626 __FUNCTION__, fID, t, other.fID, otherT);
1627#endif
1628 return;
1629 }
1630 }
caryclark@google.com47580692012-07-23 12:14:49 +00001631#if DEBUG_ADD_T_PAIR
1632 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1633 __FUNCTION__, fID, t, other.fID, otherT);
1634#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001635 int insertedAt = addT(t, &other);
1636 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001637 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001638 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001639 matchWindingValue(insertedAt, t, borrowWind);
1640 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001641 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001642
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001643 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001644 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001645 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1646 addAngle(angles, end, start);
1647 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001648 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001649 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001650 int tIndex = nextExactSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001651 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001652 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001653 }
1654 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001655
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001656 const Bounds& bounds() const {
1657 return fBounds;
1658 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001659
caryclark@google.com31143cf2012-11-09 22:14:19 +00001660 void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001661 double referenceT = fTs[index].fT;
1662 int lesser = index;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001663 while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand)
1664 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00001665 buildAnglesInner(lesser, angles);
1666 }
1667 do {
1668 buildAnglesInner(index, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001669 } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand)
1670 && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001671 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001672
1673 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1674 Span* span = &fTs[index];
1675 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001676 // if there is only one live crossing, and no coincidence, continue
1677 // in the same direction
1678 // if there is coincidence, the only choice may be to reverse direction
1679 // find edge on either side of intersection
1680 int oIndex = span->fOtherIndex;
1681 // if done == -1, prior span has already been processed
1682 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001683 int next = other->nextExactSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001684 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001685 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001686 next = other->nextExactSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001687 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001688 // add candidate into and away from junction
1689 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001690 }
1691
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001692 int computeSum(int startIndex, int endIndex) {
1693 SkTDArray<Angle> angles;
1694 addTwoAngles(startIndex, endIndex, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001695 buildAngles(endIndex, angles, false);
caryclark@google.comd1688742012-09-18 20:08:37 +00001696 // OPTIMIZATION: check all angles to see if any have computed wind sum
1697 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001698 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001699 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001700#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001701 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001702#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001703 if (!sortable) {
1704 return SK_MinS32;
1705 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001706 int angleCount = angles.count();
1707 const Angle* angle;
1708 const Segment* base;
1709 int winding;
1710 int firstIndex = 0;
1711 do {
1712 angle = sorted[firstIndex];
1713 base = angle->segment();
1714 winding = base->windSum(angle);
1715 if (winding != SK_MinS32) {
1716 break;
1717 }
1718 if (++firstIndex == angleCount) {
1719 return SK_MinS32;
1720 }
1721 } while (true);
1722 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001723 int spanWinding = base->spanSign(angle);
1724 bool inner = useInnerWinding(winding + spanWinding, winding);
1725 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001726 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001727 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001728 inner ? winding + spanWinding : winding);
1729 #endif
1730 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001731 winding += spanWinding;
1732 }
1733 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001734 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001735 #endif
1736 int nextIndex = firstIndex + 1;
1737 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001738 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001739 do {
1740 if (nextIndex == angleCount) {
1741 nextIndex = 0;
1742 }
1743 angle = sorted[nextIndex];
1744 Segment* segment = angle->segment();
1745 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001746 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001747 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001748 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001749 maxWinding = winding;
1750 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001751 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001752 }
1753 } while (++nextIndex != lastIndex);
1754 return windSum(SkMin32(startIndex, endIndex));
1755 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001756
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001757 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001758 int bestT = -1;
1759 SkScalar top = bounds().fTop;
1760 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001761 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001762 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001763 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001764 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001765 if (fTs[start].fWindValue == 0) {
1766 continue;
1767 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001768 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001769 double startT = fTs[start].fT;
1770 double endT = fTs[end].fT;
1771 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001772 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001773 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001774 // FIXME: always use original and limit results to T values within
1775 // start t and end t.
1776 // OPTIMIZE: use specialty function that intersects ray with curve,
1777 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001778 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1779 false, intersections);
1780 if (pts == 0) {
1781 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001782 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001783 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1784 // if the intersection is edge on, wait for another one
1785 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001786 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001787 for (int index = 0; index < pts; ++index) {
1788 SkPoint pt;
1789 double foundT = intersections.fT[0][index];
1790 double testT = startT + (endT - startT) * foundT;
1791 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1792 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001793 if (fVerb > SkPath::kLine_Verb
1794 && !approximately_less_than_zero(foundT)
1795 && !approximately_greater_than_one(foundT)) {
1796 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1797 if (approximately_zero(dx)) {
1798 continue;
1799 }
1800 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001801 bestY = pt.fY;
1802 bestT = foundT < 1 ? start : end;
1803 hitT = testT;
1804 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001805 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001806 } while (fTs[end].fT != 1);
1807 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001808 }
caryclark@google.com18063442012-07-25 12:05:18 +00001809
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001810 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1811 // if a segment is connected to this one, consider it crossing
1812 int tIndex;
1813 if (fPts[0].fX == basePt.fX) {
1814 tIndex = 0;
1815 do {
1816 const Span& sSpan = fTs[tIndex];
1817 const Segment* sOther = sSpan.fOther;
1818 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1819 continue;
1820 }
1821 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1822 : sOther->fBounds.fRight > basePt.fX) {
1823 return true;
1824 }
1825 } while (fTs[++tIndex].fT == 0);
1826 }
1827 if (fPts[fVerb].fX == basePt.fX) {
1828 tIndex = fTs.count() - 1;
1829 do {
1830 const Span& eSpan = fTs[tIndex];
1831 const Segment* eOther = eSpan.fOther;
1832 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1833 continue;
1834 }
1835 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1836 : eOther->fBounds.fRight > basePt.fX) {
1837 return true;
1838 }
1839 } while (fTs[--tIndex].fT == 1);
1840 }
1841 return false;
1842 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001843
caryclark@google.com18063442012-07-25 12:05:18 +00001844 bool decrementSpan(Span* span) {
1845 SkASSERT(span->fWindValue > 0);
1846 if (--(span->fWindValue) == 0) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001847 if (!span->fDone) {
1848 span->fDone = true;
1849 ++fDoneSpans;
1850 }
caryclark@google.com18063442012-07-25 12:05:18 +00001851 return true;
1852 }
1853 return false;
1854 }
1855
caryclark@google.com15fa1382012-05-07 20:49:36 +00001856 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001857 SkASSERT(fDoneSpans <= fTs.count());
1858 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001859 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001860
caryclark@google.comf839c032012-10-26 21:03:50 +00001861 bool done(int min) const {
1862 return fTs[min].fDone;
1863 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001864
caryclark@google.com47580692012-07-23 12:14:49 +00001865 bool done(const Angle& angle) const {
caryclark@google.comf839c032012-10-26 21:03:50 +00001866 return done(SkMin32(angle.start(), angle.end()));
caryclark@google.com47580692012-07-23 12:14:49 +00001867 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001868
caryclark@google.com235f56a2012-09-14 14:19:30 +00001869 Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
caryclark@google.com31143cf2012-11-09 22:14:19 +00001870 int& nextStart, int& nextEnd, int& winding, int& oppWinding,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001871 int& spanWinding, int& oppSpanWinding, bool& unsortable, ShapeOp op,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001872 const int aXorMask, const int bXorMask) {
1873 const int startIndex = nextStart;
1874 const int endIndex = nextEnd;
1875 int outerWinding = winding;
1876 int innerWinding = winding + spanWinding;
1877 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00001878 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d oppWinding=%d\n",
1879 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001880 #endif
1881 if (useInnerWinding(outerWinding, innerWinding)) {
1882 outerWinding = innerWinding;
1883 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001884 int outerOppWinding = oppWinding;
1885 if (oppSpanWinding) {
1886 int innerOppWinding = oppWinding + oppSpanWinding;
1887 if (useInnerWinding(oppWinding, innerOppWinding)) {
1888 outerOppWinding = innerOppWinding;
1889 }
1890 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001891 SkASSERT(startIndex != endIndex);
1892 int count = fTs.count();
1893 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1894 : startIndex > 0);
1895 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001896 int end = nextExactSpan(startIndex, step);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001897 SkASSERT(end >= 0);
1898 Span* endSpan = &fTs[end];
1899 Segment* other;
1900 if (isSimple(end)) {
1901 // mark the smaller of startIndex, endIndex done, and all adjacent
1902 // spans with the same T value (but not 'other' spans)
1903 #if DEBUG_WINDING
1904 SkDebugf("%s simple\n", __FUNCTION__);
1905 #endif
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001906 markDone(SkMin32(startIndex, endIndex), outerWinding, outerOppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001907 other = endSpan->fOther;
1908 nextStart = endSpan->fOtherIndex;
1909 double startT = other->fTs[nextStart].fT;
1910 nextEnd = nextStart;
1911 do {
1912 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001913 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00001914 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001915 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1916 return other;
1917 }
1918 // more than one viable candidate -- measure angles to find best
1919 SkTDArray<Angle> angles;
1920 SkASSERT(startIndex - endIndex != 0);
1921 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1922 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001923 buildAngles(end, angles, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001924 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001925 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001926 int angleCount = angles.count();
1927 int firstIndex = findStartingEdge(sorted, startIndex, end);
1928 SkASSERT(firstIndex >= 0);
1929 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001930 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001931 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001932 if (!sortable) {
1933 unsortable = true;
1934 return NULL;
1935 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001936 SkASSERT(sorted[firstIndex]->segment() == this);
1937 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001938 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
1939 sorted[firstIndex]->sign());
caryclark@google.com235f56a2012-09-14 14:19:30 +00001940 #endif
1941 int aSumWinding = winding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001942 int bSumWinding = oppWinding;
1943 bool angleIsOp = sorted[firstIndex]->segment()->operand() ^ operand();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001944 const Angle* firstAngle = sorted[firstIndex];
1945 int angleSpan = spanSign(firstAngle);
1946 int oppoSign = oppSign(firstAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001947 if (angleIsOp) {
1948 bSumWinding -= angleSpan;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001949 aSumWinding -= oppoSign;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001950 } else {
1951 aSumWinding -= angleSpan;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001952 bSumWinding -= oppoSign;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001953 }
1954 int nextIndex = firstIndex + 1;
1955 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1956 const Angle* foundAngle = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001957 bool foundDone = false;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001958#define TWO_CHANNEL_DONE 0
1959#if TWO_CHANNEL_DONE
caryclark@google.com235f56a2012-09-14 14:19:30 +00001960 bool foundDone2 = false;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001961#define FOUND_DONE2 foundDone2
1962#else
1963#define FOUND_DONE2 foundDone
1964#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001965 // iterate through the angle, and compute everyone's winding
caryclark@google.com31143cf2012-11-09 22:14:19 +00001966 bool aAltFlipped = false;
1967 bool bAltFlipped = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001968 bool foundFlipped = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001969 int foundSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001970 int foundOppWinding = SK_MinS32;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001971 Segment* nextSegment;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001972 int aLastNonZeroSum = winding;
1973 int bLastNonZeroSum = oppWinding;
1974 bool foundOpp;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001975 do {
1976 if (nextIndex == angleCount) {
1977 nextIndex = 0;
1978 }
1979 const Angle* nextAngle = sorted[nextIndex];
1980 nextSegment = nextAngle->segment();
caryclark@google.comf839c032012-10-26 21:03:50 +00001981 bool nextDone = nextSegment->done(*nextAngle);
1982 bool nextTiny = nextSegment->tiny(*nextAngle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001983 angleIsOp = nextSegment->operand() ^ operand();
1984 int deltaSum = nextSegment->spanSign(nextAngle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001985 int oppDeltaSum = nextSegment->oppSign(nextAngle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001986 int maxWinding, xorMask, sumWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001987 bool otherNonZero, altFlipped, otherCoin;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001988 if (angleIsOp) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001989 maxWinding = bSumWinding;
1990 if (bSumWinding) {
1991 bLastNonZeroSum = bSumWinding;
1992 }
1993 bSumWinding -= deltaSum;
1994 sumWinding = bSumWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001995 otherNonZero = aSumWinding & aXorMask;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001996 xorMask = bXorMask;
1997 bAltFlipped ^= bLastNonZeroSum * bSumWinding < 0; // flip if different signs
1998 altFlipped = bAltFlipped;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001999 aSumWinding -= oppDeltaSum;
2000 otherCoin = aSumWinding & aXorMask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002001 } else {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002002 maxWinding = aSumWinding;
2003 if (aSumWinding) {
2004 aLastNonZeroSum = aSumWinding;
2005 }
2006 aSumWinding -= deltaSum;
2007 sumWinding = aSumWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002008 otherNonZero = bSumWinding & bXorMask;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002009 xorMask = aXorMask;
2010 aAltFlipped ^= aLastNonZeroSum * aSumWinding < 0; // flip if different signs
2011 altFlipped = aAltFlipped;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002012 bSumWinding -= oppDeltaSum;
2013 otherCoin = bSumWinding & bXorMask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002014 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002015 bool opIsActive = activeOp(nextSegment->operand(), otherNonZero, otherCoin, op);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002016 int oWinding = angleIsOp ? aSumWinding : bSumWinding;
2017 if (!(sumWinding & xorMask)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002018 if (!active) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002019 markAndChaseDone(startIndex, endIndex, outerWinding, outerOppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002020 nextSegment->markAndChaseWinding(nextAngle, maxWinding, oWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002021 #if DEBUG_WINDING
2022 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
2023 #endif
2024 return NULL;
2025 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002026 if (opIsActive && (!foundAngle || foundDone)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002027 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002028 foundDone = nextDone && !nextTiny;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002029 foundFlipped = altFlipped;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002030 foundSum = 0;
2031 foundOpp = angleIsOp;
2032 foundOppWinding = oWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002033 }
2034 continue;
2035 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002036 if (opIsActive && !(maxWinding & xorMask) && (!foundAngle || FOUND_DONE2)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002037 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00002038 if (foundAngle && FOUND_DONE2) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002039 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
2040 }
2041 #endif
2042 foundAngle = nextAngle;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002043 FOUND_DONE2 = nextDone && !nextTiny;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002044 foundFlipped = altFlipped;
2045 foundSum = sumWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002046 foundOpp = angleIsOp;
2047 foundOppWinding = oWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002048 }
2049 if (nextSegment->done()) {
2050 continue;
2051 }
2052 // if the winding is non-zero, nextAngle does not connect to
2053 // current chain. If we haven't done so already, mark the angle
2054 // as done, record the winding value, and mark connected unambiguous
2055 // segments as well.
2056 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
2057 if (useInnerWinding(maxWinding, sumWinding)) {
2058 maxWinding = sumWinding;
2059 }
2060 Span* last;
2061 if (foundAngle) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002062 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding, oWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002063 } else {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002064 last = nextSegment->markAndChaseDone(nextAngle, maxWinding, oWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002065 }
2066 if (last) {
2067 *chase.append() = last;
2068 }
2069 }
2070 } while (++nextIndex != lastIndex);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002071 markDone(SkMin32(startIndex, endIndex), outerWinding, outerOppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002072 if (!foundAngle) {
2073 return NULL;
2074 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002075 #if DEBUG_WINDING
2076 int oldSpanSign = spanSign(nextStart, nextEnd);
2077 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002078 nextStart = foundAngle->start();
2079 nextEnd = foundAngle->end();
2080 nextSegment = foundAngle->segment();
2081 int flipped = foundFlipped ? -1 : 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002082 int minStartEnd = SkMin32(nextStart, nextEnd);
2083 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(minStartEnd);
2084 oppSpanWinding = SkSign32(oppSpanWinding) * flipped * nextSegment->oppValue(minStartEnd);
2085
caryclark@google.com235f56a2012-09-14 14:19:30 +00002086 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00002087 SkDebugf("%s foundFlipped=%d spanWinding=%d oldSpanSign=%d spanSign=%d\n",
2088 __FUNCTION__, foundFlipped, spanWinding, oldSpanSign,
2089 nextSegment->spanSign(foundAngle));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002090 SkDebugf("%s foundOpp=%d oppWinding=%d oppSpanWinding=%d foundOppWinding=%d winding=%d"
2091 " foundSum=", __FUNCTION__, foundOpp, oppWinding, oppSpanWinding, foundOppWinding,
2092 winding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002093 if (foundSum == SK_MinS32) {
2094 SkDebugf("?");
2095 } else {
2096 SkDebugf("%d", foundSum);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002097 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002098 SkDebugf("\n");
2099 #endif
2100 if (oppWinding != foundOppWinding) {
2101 oppWinding = foundOppWinding;
2102 if (foundOpp) {
2103 SkASSERT(foundSum != SK_MinS32);
2104 winding = foundSum;
2105 spanWinding = nextSegment->spanSign(foundAngle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002106 oppSpanWinding = nextSegment->oppSign(foundAngle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002107 }
2108 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002109 return nextSegment;
2110 }
caryclark@google.com47580692012-07-23 12:14:49 +00002111
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002112 // so the span needs to contain the pairing info found here
2113 // this should include the winding computed for the edge, and
2114 // what edge it connects to, and whether it is discarded
2115 // (maybe discarded == abs(winding) > 1) ?
2116 // only need derivatives for duration of sorting, add a new struct
2117 // for pairings, remove extra spans that have zero length and
2118 // reference an unused other
2119 // for coincident, the last span on the other may be marked done
2120 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002121
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002122 // if loop is exhausted, contour may be closed.
2123 // FIXME: pass in close point so we can check for closure
2124
2125 // given a segment, and a sense of where 'inside' is, return the next
2126 // segment. If this segment has an intersection, or ends in multiple
2127 // segments, find the mate that continues the outside.
2128 // note that if there are multiples, but no coincidence, we can limit
2129 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002130
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002131 // mark found segments as done
2132
caryclark@google.com15fa1382012-05-07 20:49:36 +00002133 // start is the index of the beginning T of this edge
2134 // it is guaranteed to have an end which describes a non-zero length (?)
2135 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002136 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002137 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2138 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002139 const int startIndex = nextStart;
2140 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002141 int outerWinding = winding;
2142 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002143 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002144 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2145 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002146 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002147 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002148 outerWinding = innerWinding;
2149 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002150 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002151 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002152 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2153 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002154 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002155 int end = nextExactSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002156 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002157 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002158 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002159 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002160 // mark the smaller of startIndex, endIndex done, and all adjacent
2161 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002162 #if DEBUG_WINDING
2163 SkDebugf("%s simple\n", __FUNCTION__);
2164 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002165 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002166 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002167 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002168 double startT = other->fTs[nextStart].fT;
2169 nextEnd = nextStart;
2170 do {
2171 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002172 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002173 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00002174 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002175 return other;
2176 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002177 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002178 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002179 SkASSERT(startIndex - endIndex != 0);
2180 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002181 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002182 buildAngles(end, angles, false);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002183 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002184 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002185 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002186 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002187 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002188 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002189 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002190 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002191 if (!sortable) {
2192 unsortable = true;
2193 return NULL;
2194 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002195 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002196 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002197 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002198 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002199 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002200 int nextIndex = firstIndex + 1;
2201 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2202 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002203 // FIXME: found done logic probably fails if there are more than 4
2204 // sorted angles. It should bias towards the first and last undone
2205 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002206 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002207 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002208 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002209 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002210 bool altFlipped = false;
2211 bool foundFlipped = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002212 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002213 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002214 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002215 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002216 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002217 nextIndex = 0;
2218 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002219 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002220 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002221 if (sumWinding) {
2222 lastNonZeroSum = sumWinding;
2223 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002224 nextSegment = nextAngle->segment();
caryclark@google.comf839c032012-10-26 21:03:50 +00002225 bool nextDone = nextSegment->done(*nextAngle);
2226 bool nextTiny = nextSegment->tiny(*nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002227 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002228 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002229 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002230 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002231 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2232 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002233 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002234 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002235 if (!active) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002236 // FIXME: shouldn't this call mark and chase done ?
caryclark@google.com59823f72012-08-09 18:17:47 +00002237 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002238 // FIXME: shouldn't this call mark and chase winding ?
caryclark@google.com47580692012-07-23 12:14:49 +00002239 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002240 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002241 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002242 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002243 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002244 return NULL;
2245 }
caryclark@google.com47580692012-07-23 12:14:49 +00002246 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002247 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002248 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002249 foundFlipped = altFlipped;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002250 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002251 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002252 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002253
caryclark@google.com24bec792012-08-20 12:43:57 +00002254 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002255 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002256 if (foundAngle && foundDone2) {
2257 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002258 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002259 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002260 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002261 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002262 foundFlipped = altFlipped;
2263 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002264 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002265 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002266 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002267 }
2268 // if the winding is non-zero, nextAngle does not connect to
2269 // current chain. If we haven't done so already, mark the angle
2270 // as done, record the winding value, and mark connected unambiguous
2271 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002272 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002273 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002274 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002275 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002276 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002277 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002278 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002279 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002280 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002281 }
2282 if (last) {
2283 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002284 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002285 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002286 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002287 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002288 if (!foundAngle) {
2289 return NULL;
2290 }
2291 nextStart = foundAngle->start();
2292 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002293 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002294 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002295 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2296 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002297 if (winding) {
2298 #if DEBUG_WINDING
2299 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2300 if (foundSum == SK_MinS32) {
2301 SkDebugf("?");
2302 } else {
2303 SkDebugf("%d", foundSum);
2304 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002305 SkDebugf("\n");
2306 #endif
2307 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002308 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002309 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002310 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002311 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002312 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002313 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002314
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002315 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002316 const int startIndex = nextStart;
2317 const int endIndex = nextEnd;
2318 SkASSERT(startIndex != endIndex);
2319 int count = fTs.count();
2320 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2321 : startIndex > 0);
2322 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002323 int end = nextExactSpan(startIndex, step);
caryclark@google.com24bec792012-08-20 12:43:57 +00002324 SkASSERT(end >= 0);
2325 Span* endSpan = &fTs[end];
2326 Segment* other;
2327 markDone(SkMin32(startIndex, endIndex), 1);
2328 if (isSimple(end)) {
2329 #if DEBUG_WINDING
2330 SkDebugf("%s simple\n", __FUNCTION__);
2331 #endif
2332 other = endSpan->fOther;
2333 nextStart = endSpan->fOtherIndex;
2334 double startT = other->fTs[nextStart].fT;
2335 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002336 if ((approximately_less_than_zero(startT) && step < 0)
2337 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002338 step = -step;
2339 SkDEBUGCODE(firstLoop = false;)
2340 }
2341 do {
2342 nextEnd = nextStart;
2343 do {
2344 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002345 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002346 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002347 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2348 break;
2349 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002350 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002351 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002352 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002353 SkDEBUGCODE(firstLoop = false;)
2354 step = -step;
2355 } while (true);
2356 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2357 return other;
2358 }
2359 SkTDArray<Angle> angles;
2360 SkASSERT(startIndex - endIndex != 0);
2361 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2362 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002363 buildAngles(end, angles, false);
caryclark@google.com24bec792012-08-20 12:43:57 +00002364 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002365 bool sortable = SortAngles(angles, sorted);
caryclark@google.com24bec792012-08-20 12:43:57 +00002366 int angleCount = angles.count();
2367 int firstIndex = findStartingEdge(sorted, startIndex, end);
2368 SkASSERT(firstIndex >= 0);
2369 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002370 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002371 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002372 if (!sortable) {
2373 unsortable = true;
2374 return NULL;
2375 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002376 SkASSERT(sorted[firstIndex]->segment() == this);
2377 int nextIndex = firstIndex + 1;
2378 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2379 const Angle* nextAngle;
2380 Segment* nextSegment;
2381 do {
2382 if (nextIndex == angleCount) {
2383 nextIndex = 0;
2384 }
2385 nextAngle = sorted[nextIndex];
2386 nextSegment = nextAngle->segment();
2387 if (!nextSegment->done(*nextAngle)) {
2388 break;
2389 }
2390 if (++nextIndex == lastIndex) {
2391 return NULL;
2392 }
2393 } while (true);
2394 nextStart = nextAngle->start();
2395 nextEnd = nextAngle->end();
2396 return nextSegment;
2397 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002398
2399 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2400 int angleCount = sorted.count();
2401 int firstIndex = -1;
2402 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2403 const Angle* angle = sorted[angleIndex];
2404 if (angle->segment() == this && angle->start() == end &&
2405 angle->end() == start) {
2406 firstIndex = angleIndex;
2407 break;
2408 }
2409 }
2410 return firstIndex;
2411 }
2412
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002413 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002414 void findTooCloseToCall(bool thisXor, bool otherXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002415 int count = fTs.count();
2416 if (count < 3) { // require t=0, x, 1 at minimum
2417 return;
2418 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002419 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002420 int moCount;
2421 Span* match;
2422 Segment* mOther;
2423 do {
2424 match = &fTs[matchIndex];
2425 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002426 // FIXME: allow quads, cubics to be near coincident?
2427 if (mOther->fVerb == SkPath::kLine_Verb) {
2428 moCount = mOther->fTs.count();
2429 if (moCount >= 3) {
2430 break;
2431 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002432 }
2433 if (++matchIndex >= count) {
2434 return;
2435 }
2436 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002437 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002438 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002439 // look for a pair of nearby T values that map to the same (x,y) value
2440 // if found, see if the pair of other segments share a common point. If
2441 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002442 for (int index = matchIndex + 1; index < count; ++index) {
2443 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002444 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002445 continue;
2446 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002447 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002448 if (tOther->fVerb != SkPath::kLine_Verb) {
2449 continue; // FIXME: allow quads, cubics to be near coincident?
2450 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002451 int toCount = tOther->fTs.count();
2452 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002453 continue;
2454 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002455 const SkPoint* testPt = &xyAtT(test);
2456 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002457 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002458 moCount = toCount;
2459 match = test;
2460 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002461 matchPt = testPt;
2462 continue;
2463 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002464 int moStart = -1;
2465 int moEnd = -1;
2466 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002467 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002468 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002469 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002470 continue;
2471 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002472 if (moSpan.fOther == this) {
2473 if (moSpan.fOtherT == match->fT) {
2474 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002475 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002476 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002477 continue;
2478 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002479 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002480 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2481 moStart = -1;
2482 break;
2483 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002484 SkASSERT(moEnd == -1);
2485 moEnd = moIndex;
2486 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002487 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002488 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002489 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002490 continue;
2491 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002492 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002493 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002494 continue;
2495 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002496 int toStart = -1;
2497 int toEnd = -1;
2498 double toStartT, toEndT;
2499 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2500 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002501 if (toSpan.fDone) {
2502 continue;
2503 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002504 if (toSpan.fOther == this) {
2505 if (toSpan.fOtherT == test->fT) {
2506 toStart = toIndex;
2507 toStartT = toSpan.fT;
2508 }
2509 continue;
2510 }
2511 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002512 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2513 moStart = -1;
2514 break;
2515 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002516 SkASSERT(toEnd == -1);
2517 toEnd = toIndex;
2518 toEndT = toSpan.fT;
2519 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002520 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002521 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2522 if (toStart <= 0 || toEnd <= 0) {
2523 continue;
2524 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002525 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002526 continue;
2527 }
2528 // test to see if the segment between there and here is linear
2529 if (!mOther->isLinear(moStart, moEnd)
2530 || !tOther->isLinear(toStart, toEnd)) {
2531 continue;
2532 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002533 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002534 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002535 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002536 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002537 // FIXME: this is bogus for multiple ops
2538 // the xorMask needs to be accumulated from the union of the two
2539 // edges -- which means that the segment must have its own copy of the mask
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002540 mOther->addTCoincident(thisXor, otherXor,
2541 moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002542 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002543 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002544 }
2545
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002546 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002547 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002548 // a) mark spans with either end unsortable as done, or
2549 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2550 // when encountering an unsortable span
2551
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002552 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2553 // and use more concise logic like the old edge walker code?
2554 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002555 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002556 // iterate through T intersections and return topmost
2557 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002558 SkASSERT(!done());
2559 int firstT;
2560 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002561 SkPoint topPt;
2562 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002563 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002564 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002565 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002566 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002567 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002568 const Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00002569 if (span.fUnsortableStart | lastUnsortable) {
2570 goto next;
2571 }
2572 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002573 const SkPoint& intercept = xyAtT(&span);
2574 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2575 && topPt.fX > intercept.fX)) {
2576 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002577 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002578 } else if (topPt == intercept) {
2579 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002580 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002581 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002582 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002583 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002584 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002585 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002586 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002587 int step = 1;
2588 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002589 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002590 step = -1;
2591 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002592 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002593 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002594 // if the topmost T is not on end, or is three-way or more, find left
2595 // look for left-ness from tLeft to firstT (matching y of other)
2596 SkTDArray<Angle> angles;
2597 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002598 addTwoAngles(end, firstT, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002599 buildAngles(firstT, angles, true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002600 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002601 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002602 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002603 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002604 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00002605 if (!sortable) {
2606 return NULL;
2607 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002608 // skip edges that have already been processed
2609 firstT = -1;
2610 Segment* leftSegment;
2611 do {
2612 const Angle* angle = sorted[++firstT];
caryclark@google.comf839c032012-10-26 21:03:50 +00002613 SkASSERT(!angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002614 leftSegment = angle->segment();
2615 tIndex = angle->end();
2616 endIndex = angle->start();
2617 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002618 return leftSegment;
2619 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002620
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002621 // FIXME: not crazy about this
2622 // when the intersections are performed, the other index is into an
2623 // incomplete array. as the array grows, the indices become incorrect
2624 // while the following fixes the indices up again, it isn't smart about
2625 // skipping segments whose indices are already correct
2626 // assuming we leave the code that wrote the index in the first place
2627 void fixOtherTIndex() {
2628 int iCount = fTs.count();
2629 for (int i = 0; i < iCount; ++i) {
2630 Span& iSpan = fTs[i];
2631 double oT = iSpan.fOtherT;
2632 Segment* other = iSpan.fOther;
2633 int oCount = other->fTs.count();
2634 for (int o = 0; o < oCount; ++o) {
2635 Span& oSpan = other->fTs[o];
2636 if (oT == oSpan.fT && this == oSpan.fOther) {
2637 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002638 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002639 }
2640 }
2641 }
2642 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002643
caryclark@google.com495f8e42012-05-31 13:13:11 +00002644 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002645 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002646 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002647 SkASSERT(end >= 0);
2648 if (multipleSpans(end)) {
2649 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002650 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002651 const Span& endSpan = fTs[end];
2652 Segment* other = endSpan.fOther;
2653 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002654 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002655 Span* last = other->innerChaseDone(index, step, winding);
2656 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002657 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002658 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002659
caryclark@google.com31143cf2012-11-09 22:14:19 +00002660 Span* innerChaseDone(int index, int step, int winding, int oppWinding) {
2661 int end = nextExactSpan(index, step);
2662 SkASSERT(end >= 0);
2663 if (multipleSpans(end)) {
2664 return &fTs[end];
2665 }
2666 const Span& endSpan = fTs[end];
2667 Segment* other = endSpan.fOther;
2668 index = endSpan.fOtherIndex;
2669 int otherEnd = other->nextExactSpan(index, step);
2670 Span* last = other->innerChaseDone(index, step, winding, oppWinding);
2671 other->markDone(SkMin32(index, otherEnd), winding, oppWinding);
2672 return last;
2673 }
2674
2675
caryclark@google.com59823f72012-08-09 18:17:47 +00002676 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002677 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002678 SkASSERT(end >= 0);
2679 if (multipleSpans(end)) {
2680 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002681 }
2682 const Span& endSpan = fTs[end];
2683 Segment* other = endSpan.fOther;
2684 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002685 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002686 int min = SkMin32(index, otherEnd);
2687 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002688 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002689 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002690 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002691 Span* last = other->innerChaseWinding(index, step, winding);
2692 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002693 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002694 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002695
caryclark@google.com31143cf2012-11-09 22:14:19 +00002696 Span* innerChaseWinding(int index, int step, int winding, int oppWinding) {
2697 int end = nextExactSpan(index, step);
2698 SkASSERT(end >= 0);
2699 if (multipleSpans(end)) {
2700 return &fTs[end];
2701 }
2702 const Span& endSpan = fTs[end];
2703 Segment* other = endSpan.fOther;
2704 index = endSpan.fOtherIndex;
2705 int otherEnd = other->nextExactSpan(index, step);
2706 int min = SkMin32(index, otherEnd);
2707 if (other->fTs[min].fWindSum != SK_MinS32) {
2708 SkASSERT(other->fTs[min].fWindSum == winding);
2709 return NULL;
2710 }
2711 Span* last = other->innerChaseWinding(index, step, winding, oppWinding);
2712 other->markWinding(min, winding, oppWinding);
2713 return last;
2714 }
2715
caryclark@google.com235f56a2012-09-14 14:19:30 +00002716 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2717 fDoneSpans = 0;
2718 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002719 fPts = pts;
2720 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002721 }
2722
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002723 bool intersected() const {
2724 return fTs.count() > 0;
2725 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002726
2727 bool isConnected(int startIndex, int endIndex) const {
2728 return fTs[startIndex].fWindSum != SK_MinS32
2729 || fTs[endIndex].fWindSum != SK_MinS32;
2730 }
2731
caryclark@google.com235f56a2012-09-14 14:19:30 +00002732 bool isHorizontal() const {
2733 return fBounds.fTop == fBounds.fBottom;
2734 }
2735
caryclark@google.com15fa1382012-05-07 20:49:36 +00002736 bool isLinear(int start, int end) const {
2737 if (fVerb == SkPath::kLine_Verb) {
2738 return true;
2739 }
2740 if (fVerb == SkPath::kQuad_Verb) {
2741 SkPoint qPart[3];
2742 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2743 return QuadIsLinear(qPart);
2744 } else {
2745 SkASSERT(fVerb == SkPath::kCubic_Verb);
2746 SkPoint cPart[4];
2747 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2748 return CubicIsLinear(cPart);
2749 }
2750 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002751
2752 // OPTIMIZE: successive calls could start were the last leaves off
2753 // or calls could specialize to walk forwards or backwards
2754 bool isMissing(double startT) const {
2755 size_t tCount = fTs.count();
2756 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002757 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002758 return false;
2759 }
2760 }
2761 return true;
2762 }
2763
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002764 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002765 int count = fTs.count();
2766 if (count == 2) {
2767 return true;
2768 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002769 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002770 if (approximately_less_than_zero(t)) {
2771 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002772 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002773 if (approximately_greater_than_one(t)) {
2774 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002775 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002776 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002777 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002778
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002779 bool isVertical() const {
2780 return fBounds.fLeft == fBounds.fRight;
2781 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002782
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002783 SkScalar leftMost(int start, int end) const {
2784 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2785 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002786
caryclark@google.com495f8e42012-05-31 13:13:11 +00002787 // this span is excluded by the winding rule -- chase the ends
2788 // as long as they are unambiguous to mark connections as done
2789 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002790 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002791 int index = angle->start();
2792 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002793 return markAndChaseDone(index, endIndex, winding);
2794 }
2795
2796 Span* markAndChaseDone(const Angle* angle, int winding, int oppWinding) {
2797 int index = angle->start();
2798 int endIndex = angle->end();
2799 return markAndChaseDone(index, endIndex, winding, oppWinding);
2800 }
2801
2802 Span* markAndChaseDone(int index, int endIndex, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002803 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002804 Span* last = innerChaseDone(index, step, winding);
2805 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002806 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002807 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002808
caryclark@google.com31143cf2012-11-09 22:14:19 +00002809 Span* markAndChaseDone(int index, int endIndex, int winding, int oppWinding) {
2810 int step = SkSign32(endIndex - index);
2811 Span* last = innerChaseDone(index, step, winding, oppWinding);
2812 markDone(SkMin32(index, endIndex), winding, oppWinding);
2813 return last;
2814 }
2815
caryclark@google.com59823f72012-08-09 18:17:47 +00002816 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002817 int index = angle->start();
2818 int endIndex = angle->end();
2819 int min = SkMin32(index, endIndex);
2820 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002821 Span* last = innerChaseWinding(index, step, winding);
2822 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002823 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002824 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002825
caryclark@google.com31143cf2012-11-09 22:14:19 +00002826 Span* markAndChaseWinding(const Angle* angle, int winding, int oppWinding) {
2827 int index = angle->start();
2828 int endIndex = angle->end();
2829 int min = SkMin32(index, endIndex);
2830 int step = SkSign32(endIndex - index);
2831 Span* last = innerChaseWinding(index, step, winding, oppWinding);
2832 markWinding(min, winding, oppWinding);
2833 return last;
2834 }
2835
caryclark@google.com495f8e42012-05-31 13:13:11 +00002836 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002837 // This may be called when the segment is already marked done. While this
2838 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002839 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002840 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002841 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002842 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002843 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002844 double referenceT = fTs[index].fT;
2845 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002846 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2847 markOneDone(__FUNCTION__, lesser, winding);
2848 }
2849 do {
2850 markOneDone(__FUNCTION__, index, winding);
2851 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002852 }
2853
2854 void markDone(int index, int winding, int oppWinding) {
2855 // SkASSERT(!done());
2856 SkASSERT(winding);
2857 double referenceT = fTs[index].fT;
2858 int lesser = index;
2859 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2860 markOneDone(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002861 }
2862 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002863 markOneDone(__FUNCTION__, index, winding, oppWinding);
2864 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002865 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002866
caryclark@google.com24bec792012-08-20 12:43:57 +00002867 void markOneDone(const char* funName, int tIndex, int winding) {
2868 Span* span = markOneWinding(funName, tIndex, winding);
2869 if (!span) {
2870 return;
2871 }
2872 span->fDone = true;
2873 fDoneSpans++;
2874 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002875
caryclark@google.com31143cf2012-11-09 22:14:19 +00002876 void markOneDone(const char* funName, int tIndex, int winding, int oppWinding) {
2877 Span* span = markOneWinding(funName, tIndex, winding, oppWinding);
2878 if (!span) {
2879 return;
2880 }
2881 span->fDone = true;
2882 fDoneSpans++;
2883 }
2884
caryclark@google.com24bec792012-08-20 12:43:57 +00002885 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2886 Span& span = fTs[tIndex];
2887 if (span.fDone) {
2888 return NULL;
2889 }
2890 #if DEBUG_MARK_DONE
2891 debugShowNewWinding(funName, span, winding);
2892 #endif
2893 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002894 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002895 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002896 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002897 span.fWindSum = winding;
2898 return &span;
2899 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002900
caryclark@google.com31143cf2012-11-09 22:14:19 +00002901 Span* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding) {
2902 Span& span = fTs[tIndex];
2903 if (span.fDone) {
2904 return NULL;
2905 }
2906 #if DEBUG_MARK_DONE
2907 debugShowNewWinding(funName, span, winding, oppWinding);
2908 #endif
2909 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
2910 #ifdef SK_DEBUG
2911 SkASSERT(abs(winding) <= gDebugMaxWindSum);
2912 #endif
2913 span.fWindSum = winding;
2914 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
2915 #ifdef SK_DEBUG
2916 SkASSERT(abs(oppWinding) <= gDebugMaxWindSum);
2917 #endif
2918 span.fOppSum = oppWinding;
2919 return &span;
2920 }
2921
caryclark@google.comf839c032012-10-26 21:03:50 +00002922 // note that just because a span has one end that is unsortable, that's
2923 // not enough to mark it done. The other end may be sortable, allowing the
2924 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002925 void markUnsortable(int start, int end) {
2926 Span* span = &fTs[start];
2927 if (start < end) {
2928 span->fUnsortableStart = true;
2929 } else {
2930 --span;
2931 span->fUnsortableEnd = true;
2932 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002933 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002934 return;
2935 }
2936 span->fDone = true;
2937 fDoneSpans++;
2938 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002939
caryclark@google.com59823f72012-08-09 18:17:47 +00002940 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002941 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002942 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002943 double referenceT = fTs[index].fT;
2944 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002945 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2946 markOneWinding(__FUNCTION__, lesser, winding);
2947 }
2948 do {
2949 markOneWinding(__FUNCTION__, index, winding);
2950 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002951 }
2952
2953 void markWinding(int index, int winding, int oppWinding) {
2954 // SkASSERT(!done());
2955 SkASSERT(winding);
2956 double referenceT = fTs[index].fT;
2957 int lesser = index;
2958 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2959 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002960 }
2961 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002962 markOneWinding(__FUNCTION__, index, winding, oppWinding);
2963 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002964 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002965
caryclark@google.com2ddff932012-08-07 21:25:27 +00002966 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002967 int nextDoorWind = SK_MaxS32;
2968 if (tIndex > 0) {
2969 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002970 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002971 nextDoorWind = below.fWindValue;
2972 }
2973 }
2974 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2975 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002976 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002977 nextDoorWind = above.fWindValue;
2978 }
2979 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002980 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2981 const Span& below = fTs[tIndex - 1];
2982 nextDoorWind = below.fWindValue;
2983 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002984 if (nextDoorWind != SK_MaxS32) {
2985 Span& newSpan = fTs[tIndex];
2986 newSpan.fWindValue = nextDoorWind;
caryclark@google.comf839c032012-10-26 21:03:50 +00002987 if (!nextDoorWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002988 newSpan.fDone = true;
2989 ++fDoneSpans;
2990 }
2991 }
2992 }
2993
caryclark@google.com9764cc62012-07-12 19:29:45 +00002994 // return span if when chasing, two or more radiating spans are not done
2995 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2996 // candidate and the remaining spans have windValue == 0 (canceled by
2997 // coincidence). The coincident edges could either be removed altogether,
2998 // or this code could be more complicated in detecting this case. Worth it?
2999 bool multipleSpans(int end) const {
3000 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003001 }
3002
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003003 // This has callers for two different situations: one establishes the end
3004 // of the current span, and one establishes the beginning of the next span
3005 // (thus the name). When this is looking for the end of the current span,
3006 // coincidence is found when the beginning Ts contain -step and the end
3007 // contains step. When it is looking for the beginning of the next, the
3008 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003009 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003010 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003011 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00003012 int count = fTs.count();
3013 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00003014 while (step > 0 ? ++to < count : --to >= 0) {
3015 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003016 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003017 continue;
3018 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003019 return to;
3020 }
3021 return -1;
3022 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00003023
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003024 // FIXME
3025 // this returns at any difference in T, vs. a preset minimum. It may be
3026 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003027 // OPTIMIZATION splitting this into separate loops for up/down steps
3028 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003029 int nextExactSpan(int from, int step) const {
3030 const Span& fromSpan = fTs[from];
3031 int count = fTs.count();
3032 int to = from;
3033 while (step > 0 ? ++to < count : --to >= 0) {
3034 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00003035 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003036 continue;
3037 }
3038 return to;
3039 }
3040 return -1;
3041 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003042
caryclark@google.com235f56a2012-09-14 14:19:30 +00003043 bool operand() const {
3044 return fOperand;
3045 }
3046
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003047 int oppSign(const Angle* angle) const {
3048 SkASSERT(angle->segment() == this);
3049 return oppSign(angle->start(), angle->end());
3050 }
3051
3052 int oppSign(int startIndex, int endIndex) const {
3053 int result = startIndex < endIndex ? -fTs[startIndex].fOppValue
3054 : fTs[endIndex].fOppValue;
3055#if DEBUG_WIND_BUMP
3056 SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
3057#endif
3058 return result;
3059 }
3060
caryclark@google.com31143cf2012-11-09 22:14:19 +00003061 int oppSum(int tIndex) const {
3062 return fTs[tIndex].fOppSum;
3063 }
3064
3065 int oppSum(const Angle* angle) const {
3066 int lesser = SkMin32(angle->start(), angle->end());
3067 return fTs[lesser].fOppSum;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003068 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003069
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003070 int oppValue(int tIndex) const {
3071 return fTs[tIndex].fOppValue;
3072 }
3073
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003074 const SkPoint* pts() const {
3075 return fPts;
3076 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003077
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003078 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003079 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003080 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
3081 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003082 }
3083
caryclark@google.comf839c032012-10-26 21:03:50 +00003084 // This marks all spans unsortable so that this info is available for early
3085 // exclusion in find top and others. This could be optimized to only mark
3086 // adjacent spans that unsortable. However, this makes it difficult to later
3087 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003088 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003089 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003090 int angleCount = angles.count();
3091 int angleIndex;
3092 angleList.setReserve(angleCount);
3093 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003094 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00003095 *angleList.append() = &angle;
3096 sortable &= !angle.unsortable();
3097 }
3098 if (sortable) {
3099 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3100 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3101 if (angles[angleIndex].unsortable()) {
3102 sortable = false;
3103 break;
3104 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003105 }
3106 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003107 if (!sortable) {
3108 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3109 Angle& angle = angles[angleIndex];
3110 angle.segment()->markUnsortable(angle.start(), angle.end());
3111 }
3112 }
3113 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003114 }
3115
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003116 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003117 const Span& span(int tIndex) const {
3118 return fTs[tIndex];
3119 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003120
caryclark@google.com235f56a2012-09-14 14:19:30 +00003121 int spanSign(const Angle* angle) const {
3122 SkASSERT(angle->segment() == this);
3123 return spanSign(angle->start(), angle->end());
3124 }
3125
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003126 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003127 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue
3128 : fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003129#if DEBUG_WIND_BUMP
3130 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3131#endif
3132 return result;
3133 }
3134
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003135 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003136 double t(int tIndex) const {
3137 return fTs[tIndex].fT;
3138 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003139
caryclark@google.comf839c032012-10-26 21:03:50 +00003140 bool tiny(const Angle& angle) const {
3141 int start = angle.start();
3142 int end = angle.end();
3143 const Span& mSpan = fTs[SkMin32(start, end)];
3144 return mSpan.fTiny;
3145 }
3146
caryclark@google.com18063442012-07-25 12:05:18 +00003147 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3148 double start) {
3149 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003150 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003151 *outsideTs.append() = end;
3152 *outsideTs.append() = start;
3153 }
3154 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003155
caryclark@google.com24bec792012-08-20 12:43:57 +00003156 void undoneSpan(int& start, int& end) {
3157 size_t tCount = fTs.count();
3158 size_t index;
3159 for (index = 0; index < tCount; ++index) {
3160 if (!fTs[index].fDone) {
3161 break;
3162 }
3163 }
3164 SkASSERT(index < tCount - 1);
3165 start = index;
3166 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003167 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003168 SkASSERT(index < tCount);
3169 SkASSERT(index < tCount);
3170 end = index;
3171 }
caryclark@google.com18063442012-07-25 12:05:18 +00003172
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003173 bool unsortable(int index) const {
3174 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3175 }
3176
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003177 void updatePts(const SkPoint pts[]) {
3178 fPts = pts;
3179 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003180
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003181 SkPath::Verb verb() const {
3182 return fVerb;
3183 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003184
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003185 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003186 return fTs[tIndex].fWindSum;
3187 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003188
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003189 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003190 int start = angle->start();
3191 int end = angle->end();
3192 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003193 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003194 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003195
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003196 int windValue(int tIndex) const {
3197 return fTs[tIndex].fWindValue;
3198 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003199
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003200 int windValue(const Angle* angle) const {
3201 int start = angle->start();
3202 int end = angle->end();
3203 int index = SkMin32(start, end);
3204 return windValue(index);
3205 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003206
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003207 SkScalar xAtT(const Span* span) const {
3208 return xyAtT(span).fX;
3209 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003210
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003211 const SkPoint& xyAtT(int index) const {
3212 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003213 }
3214
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003215 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003216 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003217 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003218 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003219 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003220 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003221 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003222 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003223 }
3224 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003225 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003226 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003227
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003228 SkScalar yAtT(int index) const {
3229 return yAtT(&fTs[index]);
3230 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003231
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003232 SkScalar yAtT(const Span* span) const {
3233 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003234 }
3235
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003236#if DEBUG_DUMP
3237 void dump() const {
3238 const char className[] = "Segment";
3239 const int tab = 4;
3240 for (int i = 0; i < fTs.count(); ++i) {
3241 SkPoint out;
3242 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3243 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003244 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003245 tab + sizeof(className), className, fID,
3246 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003247 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003248 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003249 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003250 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003251 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003252 }
3253#endif
3254
caryclark@google.com47580692012-07-23 12:14:49 +00003255#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003256 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003257 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003258 for (int i = 0; i < fTs.count(); ++i) {
3259 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3260 return;
3261 }
3262 }
3263 SkASSERT(0);
3264 }
3265#endif
3266
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003267#if DEBUG_DUMP
3268 int debugID() const {
3269 return fID;
3270 }
3271#endif
3272
caryclark@google.com24bec792012-08-20 12:43:57 +00003273#if DEBUG_WINDING
3274 void debugShowSums() const {
3275 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3276 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3277 for (int i = 0; i < fTs.count(); ++i) {
3278 const Span& span = fTs[i];
3279 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3280 if (span.fWindSum == SK_MinS32) {
3281 SkDebugf("?");
3282 } else {
3283 SkDebugf("%d", span.fWindSum);
3284 }
3285 SkDebugf("]");
3286 }
3287 SkDebugf("\n");
3288 }
3289#endif
3290
caryclark@google.comcc905052012-07-25 20:59:42 +00003291#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003292 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003293 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00003294 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00003295 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00003296 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
3297 }
3298 SkDebugf("\n");
3299 }
3300#endif
3301
caryclark@google.com027de222012-07-12 12:52:50 +00003302#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003303 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003304 if (done()) {
3305 return;
3306 }
3307 for (int i = 0; i < fTs.count(); ++i) {
3308 if (fTs[i].fDone) {
3309 continue;
3310 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003311 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003312 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3313 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3314 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3315 }
3316 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003317 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003318 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003319 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003320 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3321 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3322 if (fTs[i].fWindSum == SK_MinS32) {
3323 SkDebugf("?");
3324 } else {
3325 SkDebugf("%d", fTs[i].fWindSum);
3326 }
3327 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003328 }
3329 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003330
3331 // This isn't useful yet -- but leaving it in for now in case i think of something
3332 // to use it for
3333 void validateActiveSpans() const {
3334 if (done()) {
3335 return;
3336 }
3337 int tCount = fTs.count();
3338 for (int index = 0; index < tCount; ++index) {
3339 if (fTs[index].fDone) {
3340 continue;
3341 }
3342 // count number of connections which are not done
3343 int first = index;
3344 double baseT = fTs[index].fT;
3345 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3346 --first;
3347 }
3348 int last = index;
3349 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3350 ++last;
3351 }
3352 int connections = 0;
3353 connections += first > 0 && !fTs[first - 1].fDone;
3354 for (int test = first; test <= last; ++test) {
3355 connections += !fTs[test].fDone;
3356 const Segment* other = fTs[test].fOther;
3357 int oIndex = fTs[test].fOtherIndex;
3358 connections += !other->fTs[oIndex].fDone;
3359 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3360 }
3361 // SkASSERT(!(connections & 1));
3362 }
3363 }
caryclark@google.com027de222012-07-12 12:52:50 +00003364#endif
3365
caryclark@google.com0c803d02012-08-06 11:15:47 +00003366#if DEBUG_MARK_DONE
3367 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3368 const SkPoint& pt = xyAtT(&span);
3369 SkDebugf("%s id=%d", fun, fID);
3370 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3371 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3372 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3373 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003374 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3375 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3376 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3377 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003378 if (span.fWindSum == SK_MinS32) {
3379 SkDebugf("?");
3380 } else {
3381 SkDebugf("%d", span.fWindSum);
3382 }
3383 SkDebugf(" windValue=%d\n", span.fWindValue);
3384 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003385
3386 void debugShowNewWinding(const char* fun, const Span& span, int winding, int oppWinding) {
3387 const SkPoint& pt = xyAtT(&span);
3388 SkDebugf("%s id=%d", fun, fID);
3389 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3390 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3391 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3392 }
3393 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3394 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3395 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d newOppSum=%d oppSum=",
3396 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
3397 winding, oppWinding);
3398 if (span.fOppSum == SK_MinS32) {
3399 SkDebugf("?");
3400 } else {
3401 SkDebugf("%d", span.fOppSum);
3402 }
3403 SkDebugf(" windSum=");
3404 if (span.fWindSum == SK_MinS32) {
3405 SkDebugf("?");
3406 } else {
3407 SkDebugf("%d", span.fWindSum);
3408 }
3409 SkDebugf(" windValue=%d\n", span.fWindValue);
3410 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003411#endif
3412
caryclark@google.com47580692012-07-23 12:14:49 +00003413#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003414 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com31143cf2012-11-09 22:14:19 +00003415 const int contourWinding, const int oppContourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003416 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003417 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003418 int lastSum = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003419 int oppLastSum = oppContourWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003420 const Angle* firstAngle = angles[first];
3421 int windSum = lastSum - spanSign(firstAngle);
3422 int oppoSign = oppSign(firstAngle);
3423 int oppWindSum = oppLastSum - oppoSign;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003424 SkDebugf("%s %s contourWinding=%d oppContourWinding=%d sign=%d\n", fun, __FUNCTION__,
3425 contourWinding, oppContourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003426 int index = first;
3427 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003428 do {
3429 const Angle& angle = *angles[index];
3430 const Segment& segment = *angle.segment();
3431 int start = angle.start();
3432 int end = angle.end();
3433 const Span& sSpan = segment.fTs[start];
3434 const Span& eSpan = segment.fTs[end];
3435 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com31143cf2012-11-09 22:14:19 +00003436 bool opp = segment.fOperand ^ fOperand;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003437 if (!firstTime) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003438 oppoSign = segment.oppSign(&angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003439 if (opp) {
3440 oppLastSum = oppWindSum;
3441 oppWindSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003442 if (oppoSign) {
3443 lastSum = windSum;
3444 windSum -= oppoSign;
3445 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003446 } else {
3447 lastSum = windSum;
3448 windSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003449 if (oppoSign) {
3450 oppLastSum = oppWindSum;
3451 oppWindSum -= oppoSign;
3452 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003453 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00003454 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003455 SkDebugf("%s [%d] %sid=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00003456 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003457 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003458 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003459 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3460 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3461 mSpan.fWindValue);
3462 if (mSpan.fWindSum == SK_MinS32) {
3463 SkDebugf("?");
3464 } else {
3465 SkDebugf("%d", mSpan.fWindSum);
3466 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003467 int last, wind;
3468 if (opp) {
3469 last = oppLastSum;
3470 wind = oppWindSum;
3471 } else {
3472 last = lastSum;
3473 wind = windSum;
3474 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003475 if (!oppoSign) {
3476 SkDebugf(" %d->%d (max=%d)", last, wind,
3477 useInnerWinding(last, wind) ? wind : last);
3478 } else {
3479 SkDebugf(" %d->%d (%d->%d)", last, wind, opp ? lastSum : oppLastSum,
3480 opp ? windSum : oppWindSum);
3481 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003482 SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003483#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003484 angle.debugShow(segment.xyAtT(&sSpan));
3485#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003486 ++index;
3487 if (index == angles.count()) {
3488 index = 0;
3489 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003490 if (firstTime) {
3491 firstTime = false;
3492 }
caryclark@google.com47580692012-07-23 12:14:49 +00003493 } while (index != first);
3494 }
3495#endif
3496
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003497#if DEBUG_WINDING
3498 bool debugVerifyWinding(int start, int end, int winding) const {
3499 const Span& span = fTs[SkMin32(start, end)];
3500 int spanWinding = span.fWindSum;
3501 if (spanWinding == SK_MinS32) {
3502 return true;
3503 }
3504 int spanSign = SkSign32(start - end);
3505 int signedVal = spanSign * span.fWindValue;
3506 if (signedVal < 0) {
3507 spanWinding -= signedVal;
3508 }
3509 return span.fWindSum == winding;
3510 }
3511#endif
3512
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003513private:
3514 const SkPoint* fPts;
3515 SkPath::Verb fVerb;
3516 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003517 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00003518 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00003519 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003520#if DEBUG_DUMP
3521 int fID;
3522#endif
3523};
3524
caryclark@google.comb9738012012-07-03 19:53:30 +00003525class Contour;
3526
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003527struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003528 Contour* fContours[2];
3529 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003530 double fTs[2][2];
3531};
3532
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003533class Contour {
3534public:
3535 Contour() {
3536 reset();
3537#if DEBUG_DUMP
3538 fID = ++gContourID;
3539#endif
3540 }
3541
3542 bool operator<(const Contour& rh) const {
3543 return fBounds.fTop == rh.fBounds.fTop
3544 ? fBounds.fLeft < rh.fBounds.fLeft
3545 : fBounds.fTop < rh.fBounds.fTop;
3546 }
3547
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003548 void addCoincident(int index, Contour* other, int otherIndex,
3549 const Intersections& ts, bool swap) {
3550 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003551 coincidence.fContours[0] = this; // FIXME: no need to store
caryclark@google.comb9738012012-07-03 19:53:30 +00003552 coincidence.fContours[1] = other;
3553 coincidence.fSegments[0] = index;
3554 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003555 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3556 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3557 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3558 coincidence.fTs[swap][0] = ts.fT[0][0];
3559 coincidence.fTs[swap][1] = ts.fT[0][1];
3560 coincidence.fTs[!swap][0] = ts.fT[1][0];
3561 coincidence.fTs[!swap][1] = ts.fT[1][1];
3562 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3563 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3564 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3565 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3566 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3567 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3568 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003569 }
3570
3571 void addCross(const Contour* crosser) {
3572#ifdef DEBUG_CROSS
3573 for (int index = 0; index < fCrosses.count(); ++index) {
3574 SkASSERT(fCrosses[index] != crosser);
3575 }
3576#endif
3577 *fCrosses.append() = crosser;
3578 }
3579
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003580 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003581 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003582 fContainsCurves = true;
3583 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003584
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003585 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003586 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003587 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003588 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003589
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003590 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3591 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3592 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003593
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003594 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003595 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003596 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003597 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003598 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003599
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003600 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3601 containsIntercepts();
3602 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3603 }
3604
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003605 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003606 return fBounds;
3607 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003608
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003609 void complete() {
3610 setBounds();
3611 fContainsIntercepts = false;
3612 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003613
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003614 void containsIntercepts() {
3615 fContainsIntercepts = true;
3616 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003617
rmistry@google.comd6176b02012-08-23 18:14:13 +00003618 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003619 int &tIndex, double& hitT) {
3620 int segmentCount = fSegments.count();
3621 const Segment* bestSegment = NULL;
3622 for (int test = 0; test < segmentCount; ++test) {
3623 Segment* testSegment = &fSegments[test];
3624 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003625 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003626 continue;
3627 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003628 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003629 continue;
3630 }
3631 if (bounds.fLeft > basePt.fX) {
3632 continue;
3633 }
3634 if (bounds.fRight < basePt.fX) {
3635 continue;
3636 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003637 if (bounds.fLeft == bounds.fRight) {
3638 continue;
3639 }
3640 #if 0
3641 bool leftHalf = bounds.fLeft == basePt.fX;
3642 bool rightHalf = bounds.fRight == basePt.fX;
3643 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3644 basePt, leftHalf, rightHalf)) {
3645 continue;
3646 }
3647 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003648 double testHitT;
3649 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3650 if (testT >= 0) {
3651 bestSegment = testSegment;
3652 tIndex = testT;
3653 hitT = testHitT;
3654 }
3655 }
3656 return bestSegment;
3657 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003658
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003659 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003660 for (int index = 0; index < fCrosses.count(); ++index) {
3661 if (fCrosses[index] == crosser) {
3662 return true;
3663 }
3664 }
3665 return false;
3666 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003667
caryclark@google.comf839c032012-10-26 21:03:50 +00003668 const SkPoint& end() const {
3669 const Segment& segment = fSegments.back();
3670 return segment.pts()[segment.verb()];
3671 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003672
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003673 void findTooCloseToCall(bool otherXor) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003674 int segmentCount = fSegments.count();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003675 otherXor ^= fXor;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003676 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003677 fSegments[sIndex].findTooCloseToCall(fXor, otherXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003678 }
3679 }
3680
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003681 void fixOtherTIndex() {
3682 int segmentCount = fSegments.count();
3683 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3684 fSegments[sIndex].fixOtherTIndex();
3685 }
3686 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003687
caryclark@google.com31143cf2012-11-09 22:14:19 +00003688 bool operand() const {
3689 return fOperand;
3690 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003691
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003692 void reset() {
3693 fSegments.reset();
3694 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003695 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003696 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003697
caryclark@google.com235f56a2012-09-14 14:19:30 +00003698 // FIXME: for binary ops, need to keep both ops winding contributions separately
3699 // in edge array
3700 void resolveCoincidence() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003701 int count = fCoincidences.count();
3702 for (int index = 0; index < count; ++index) {
3703 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003704 Contour* thisContour = coincidence.fContours[0];
3705 Contour* otherContour = coincidence.fContours[1];
3706 int thisIndex = coincidence.fSegments[0];
3707 int otherIndex = coincidence.fSegments[1];
3708 Segment& thisOne = thisContour->fSegments[thisIndex];
3709 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003710 #if DEBUG_CONCIDENT
3711 thisOne.debugShowTs();
3712 other.debugShowTs();
3713 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003714 double startT = coincidence.fTs[0][0];
3715 double endT = coincidence.fTs[0][1];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003716 bool cancelers = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003717 if (startT > endT) {
3718 SkTSwap<double>(startT, endT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003719 cancelers ^= true; // FIXME: just assign true
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003720 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003721 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003722 double oStartT = coincidence.fTs[1][0];
3723 double oEndT = coincidence.fTs[1][1];
3724 if (oStartT > oEndT) {
3725 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003726 cancelers ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003727 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003728 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003729 bool opp = thisContour->fOperand ^ otherContour->fOperand;
3730 if (cancelers && !opp) {
3731 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00003732 if (startT > 0 || oEndT < 1
3733 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3734 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003735 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003736 if (oStartT > 0 || endT < 1
3737 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3738 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003739 }
3740 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003741 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003742 if (startT > 0 || oStartT > 0
3743 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003744 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003745 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003746 if (endT < 1 || oEndT < 1
3747 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003748 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003749 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003750 thisOne.addTCoincident(thisContour->fXor, otherContour->fXor,
3751 startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003752 }
caryclark@google.com47580692012-07-23 12:14:49 +00003753 #if DEBUG_CONCIDENT
3754 thisOne.debugShowTs();
3755 other.debugShowTs();
3756 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003757 }
3758 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003759
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003760 const SkTArray<Segment>& segments() {
3761 return fSegments;
3762 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003763
caryclark@google.com235f56a2012-09-14 14:19:30 +00003764 void setOperand(bool isOp) {
3765 fOperand = isOp;
3766 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003767
caryclark@google.com235f56a2012-09-14 14:19:30 +00003768 void setXor(bool isXor) {
3769 fXor = isXor;
3770 }
3771
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003772 void sortSegments() {
3773 int segmentCount = fSegments.count();
3774 fSortedSegments.setReserve(segmentCount);
3775 for (int test = 0; test < segmentCount; ++test) {
3776 *fSortedSegments.append() = &fSegments[test];
3777 }
3778 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
3779 fFirstSorted = 0;
3780 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003781
caryclark@google.comf839c032012-10-26 21:03:50 +00003782 const SkPoint& start() const {
3783 return fSegments.front().pts()[0];
3784 }
3785
3786 void toPath(PathWrapper& path) const {
3787 int segmentCount = fSegments.count();
3788 const SkPoint& pt = fSegments.front().pts()[0];
3789 path.deferredMove(pt);
3790 for (int test = 0; test < segmentCount; ++test) {
3791 fSegments[test].addCurveTo(0, 1, path, true);
3792 }
3793 path.close();
3794 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003795
caryclark@google.comf839c032012-10-26 21:03:50 +00003796 void toPartialBackward(PathWrapper& path) const {
3797 int segmentCount = fSegments.count();
3798 for (int test = segmentCount - 1; test >= 0; --test) {
3799 fSegments[test].addCurveTo(1, 0, path, true);
3800 }
3801 }
3802
3803 void toPartialForward(PathWrapper& path) const {
3804 int segmentCount = fSegments.count();
3805 for (int test = 0; test < segmentCount; ++test) {
3806 fSegments[test].addCurveTo(0, 1, path, true);
3807 }
3808 }
3809
3810#if 0 // FIXME: obsolete, remove
caryclark@google.com15fa1382012-05-07 20:49:36 +00003811 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3812 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003813 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003814 // segments' top, and not a true sort, so it could be ameniable to regular
3815 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003816 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003817 int segmentCount = fSegments.count();
3818 SkASSERT(segmentCount > 0);
3819 int best = -1;
3820 Segment* bestSegment = NULL;
3821 while (++best < segmentCount) {
3822 Segment* testSegment = &fSegments[best];
3823 if (testSegment->done()) {
3824 continue;
3825 }
3826 bestSegment = testSegment;
3827 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003828 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003829 if (!bestSegment) {
3830 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003831 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003832 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003833 for (int test = best + 1; test < segmentCount; ++test) {
3834 Segment* testSegment = &fSegments[test];
3835 if (testSegment->done()) {
3836 continue;
3837 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003838 if (testSegment->bounds().fTop > bestTop) {
3839 continue;
3840 }
3841 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003842 if (bestTop > testTop) {
3843 bestTop = testTop;
3844 bestSegment = testSegment;
3845 }
3846 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003847 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003848 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003849 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003850#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003851
caryclark@google.comf839c032012-10-26 21:03:50 +00003852 Segment* topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003853 int segmentCount = fSortedSegments.count();
3854 SkASSERT(segmentCount > 0);
3855 Segment* bestSegment = NULL;
caryclark@google.comf839c032012-10-26 21:03:50 +00003856 int sortedIndex = fFirstSorted;
3857 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
3858 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003859 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003860 if (sortedIndex == fFirstSorted) {
3861 ++fFirstSorted;
3862 }
3863 continue;
3864 }
3865 SkPoint testXY;
3866 testSegment->activeLeftTop(testXY);
3867 if (testXY.fY < topLeft.fY) {
3868 continue;
3869 }
3870 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
3871 continue;
3872 }
3873 if (bestXY.fY < testXY.fY) {
3874 continue;
3875 }
3876 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003877 continue;
3878 }
3879 bestSegment = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00003880 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003881 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003882 return bestSegment;
3883 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003884
caryclark@google.com24bec792012-08-20 12:43:57 +00003885 Segment* undoneSegment(int& start, int& end) {
3886 int segmentCount = fSegments.count();
3887 for (int test = 0; test < segmentCount; ++test) {
3888 Segment* testSegment = &fSegments[test];
3889 if (testSegment->done()) {
3890 continue;
3891 }
3892 testSegment->undoneSpan(start, end);
3893 return testSegment;
3894 }
3895 return NULL;
3896 }
3897
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003898 int updateSegment(int index, const SkPoint* pts) {
3899 Segment& segment = fSegments[index];
3900 segment.updatePts(pts);
3901 return segment.verb() + 1;
3902 }
3903
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003904#if DEBUG_TEST
3905 SkTArray<Segment>& debugSegments() {
3906 return fSegments;
3907 }
3908#endif
3909
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003910#if DEBUG_DUMP
3911 void dump() {
3912 int i;
3913 const char className[] = "Contour";
3914 const int tab = 4;
3915 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
3916 for (i = 0; i < fSegments.count(); ++i) {
3917 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
3918 className, i);
3919 fSegments[i].dump();
3920 }
3921 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
3922 tab + sizeof(className), className,
3923 fBounds.fLeft, fBounds.fTop,
3924 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003925 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
3926 className, fContainsIntercepts);
3927 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
3928 className, fContainsCurves);
3929 }
3930#endif
3931
caryclark@google.com027de222012-07-12 12:52:50 +00003932#if DEBUG_ACTIVE_SPANS
3933 void debugShowActiveSpans() {
3934 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003935 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00003936 }
3937 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003938
3939 void validateActiveSpans() {
3940 for (int index = 0; index < fSegments.count(); ++index) {
3941 fSegments[index].validateActiveSpans();
3942 }
3943 }
caryclark@google.com027de222012-07-12 12:52:50 +00003944#endif
3945
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003946protected:
3947 void setBounds() {
3948 int count = fSegments.count();
3949 if (count == 0) {
3950 SkDebugf("%s empty contour\n", __FUNCTION__);
3951 SkASSERT(0);
3952 // FIXME: delete empty contour?
3953 return;
3954 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003955 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003956 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003957 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003958 }
3959 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003960
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003961private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003962 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003963 SkTDArray<Segment*> fSortedSegments;
3964 int fFirstSorted;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003965 SkTDArray<Coincidence> fCoincidences;
3966 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003967 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003968 bool fContainsIntercepts;
3969 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003970 bool fOperand; // true for the second argument to a binary operator
3971 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003972#if DEBUG_DUMP
3973 int fID;
3974#endif
3975};
3976
3977class EdgeBuilder {
3978public:
3979
caryclark@google.comf839c032012-10-26 21:03:50 +00003980EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
3981 : fPath(path.nativePath())
3982 , fContours(contours)
3983{
3984 init();
3985}
3986
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003987EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003988 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003989 , fContours(contours)
3990{
caryclark@google.comf839c032012-10-26 21:03:50 +00003991 init();
3992}
3993
3994void init() {
3995 fCurrentContour = NULL;
3996 fOperand = false;
3997 fXorMask = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003998#if DEBUG_DUMP
3999 gContourID = 0;
4000 gSegmentID = 0;
4001#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00004002 fSecondHalf = preFetch();
4003}
4004
4005void addOperand(const SkPath& path) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004006 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
4007 fPathVerbs.pop();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004008 fPath = &path;
caryclark@google.comf839c032012-10-26 21:03:50 +00004009 fXorMask = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004010 preFetch();
4011}
4012
4013void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004014 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004015 complete();
4016 if (fCurrentContour && !fCurrentContour->segments().count()) {
4017 fContours.pop_back();
4018 }
4019 // correct pointers in contours since fReducePts may have moved as it grew
4020 int cIndex = 0;
4021 int extraCount = fExtra.count();
4022 SkASSERT(extraCount == 0 || fExtra[0] == -1);
4023 int eIndex = 0;
4024 int rIndex = 0;
4025 while (++eIndex < extraCount) {
4026 int offset = fExtra[eIndex];
4027 if (offset < 0) {
4028 ++cIndex;
4029 continue;
4030 }
4031 fCurrentContour = &fContours[cIndex];
4032 rIndex += fCurrentContour->updateSegment(offset - 1,
4033 &fReducePts[rIndex]);
4034 }
4035 fExtra.reset(); // we're done with this
4036}
4037
4038ShapeOpMask xorMask() const {
4039 return fXorMask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004040}
4041
4042protected:
4043
4044void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004045 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004046 fCurrentContour->complete();
4047 fCurrentContour = NULL;
4048 }
4049}
4050
caryclark@google.com235f56a2012-09-14 14:19:30 +00004051// FIXME:remove once we can access path pts directly
4052int preFetch() {
4053 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004054 SkPoint pts[4];
4055 SkPath::Verb verb;
4056 do {
4057 verb = iter.next(pts);
4058 *fPathVerbs.append() = verb;
4059 if (verb == SkPath::kMove_Verb) {
4060 *fPathPts.append() = pts[0];
4061 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
4062 fPathPts.append(verb, &pts[1]);
4063 }
4064 } while (verb != SkPath::kDone_Verb);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004065 return fPathVerbs.count() - 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004066}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004067
caryclark@google.com235f56a2012-09-14 14:19:30 +00004068void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004069 SkPath::Verb reducedVerb;
4070 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004071 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004072 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004073 const SkPoint* finalCurveStart = NULL;
4074 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004075 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004076 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
4077 switch (verb) {
4078 case SkPath::kMove_Verb:
4079 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004080 if (!fCurrentContour) {
4081 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004082 fCurrentContour->setOperand(fOperand);
4083 fCurrentContour->setXor(fXorMask == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004084 *fExtra.append() = -1; // start new contour
4085 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004086 finalCurveEnd = pointsPtr++;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004087 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004088 case SkPath::kLine_Verb:
4089 // skip degenerate points
4090 if (pointsPtr[-1].fX != pointsPtr[0].fX
4091 || pointsPtr[-1].fY != pointsPtr[0].fY) {
4092 fCurrentContour->addLine(&pointsPtr[-1]);
4093 }
4094 break;
4095 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00004096
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004097 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
4098 if (reducedVerb == 0) {
4099 break; // skip degenerate points
4100 }
4101 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00004102 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004103 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004104 break;
4105 }
4106 fCurrentContour->addQuad(&pointsPtr[-1]);
4107 break;
4108 case SkPath::kCubic_Verb:
4109 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
4110 if (reducedVerb == 0) {
4111 break; // skip degenerate points
4112 }
4113 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004114 *fExtra.append() =
4115 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004116 break;
4117 }
4118 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004119 *fExtra.append() =
4120 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004121 break;
4122 }
4123 fCurrentContour->addCubic(&pointsPtr[-1]);
4124 break;
4125 case SkPath::kClose_Verb:
4126 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004127 if (finalCurveStart && finalCurveEnd
4128 && *finalCurveStart != *finalCurveEnd) {
4129 *fReducePts.append() = *finalCurveStart;
4130 *fReducePts.append() = *finalCurveEnd;
4131 *fExtra.append() =
4132 fCurrentContour->addLine(fReducePts.end() - 2);
4133 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004134 complete();
caryclark@google.com31143cf2012-11-09 22:14:19 +00004135 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004136 default:
4137 SkDEBUGFAIL("bad verb");
4138 return;
4139 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004140 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004141 pointsPtr += verb;
4142 SkASSERT(fCurrentContour);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004143 nextVerb:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004144 if (verbPtr == endOfFirstHalf) {
4145 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004146 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004147 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004148}
4149
4150private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004151 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004152 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004153 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004154 Contour* fCurrentContour;
4155 SkTArray<Contour>& fContours;
4156 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004157 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com235f56a2012-09-14 14:19:30 +00004158 ShapeOpMask fXorMask;
4159 int fSecondHalf;
4160 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004161};
4162
4163class Work {
4164public:
4165 enum SegmentType {
4166 kHorizontalLine_Segment = -1,
4167 kVerticalLine_Segment = 0,
4168 kLine_Segment = SkPath::kLine_Verb,
4169 kQuad_Segment = SkPath::kQuad_Verb,
4170 kCubic_Segment = SkPath::kCubic_Verb,
4171 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004172
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004173 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4174 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4175 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004176
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004177 // FIXME: does it make sense to write otherIndex now if we're going to
4178 // fix it up later?
4179 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004180 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004181 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004182
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004183 // Avoid collapsing t values that are close to the same since
4184 // we walk ts to describe consecutive intersections. Since a pair of ts can
4185 // be nearly equal, any problems caused by this should be taken care
4186 // of later.
4187 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004188 int addT(double newT, const Work& other) {
4189 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004190 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004191
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004192 bool advance() {
4193 return ++fIndex < fLast;
4194 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004195
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004196 SkScalar bottom() const {
4197 return bounds().fBottom;
4198 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004199
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004200 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004201 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004202 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004203
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004204 const SkPoint* cubic() const {
4205 return fCubic;
4206 }
4207
4208 void init(Contour* contour) {
4209 fContour = contour;
4210 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004211 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004212 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004213
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004214 bool isAdjacent(const Work& next) {
4215 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4216 }
4217
4218 bool isFirstLast(const Work& next) {
4219 return fContour == next.fContour && fIndex == 0
4220 && next.fIndex == fLast - 1;
4221 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004222
4223 SkScalar left() const {
4224 return bounds().fLeft;
4225 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004226
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004227 void promoteToCubic() {
4228 fCubic[0] = pts()[0];
4229 fCubic[2] = pts()[1];
4230 fCubic[3] = pts()[2];
4231 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4232 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4233 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4234 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4235 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004236
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004237 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004238 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004239 }
4240
4241 SkScalar right() const {
4242 return bounds().fRight;
4243 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004244
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004245 ptrdiff_t segmentIndex() const {
4246 return fIndex;
4247 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004248
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004249 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004250 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004251 SegmentType type = (SegmentType) segment.verb();
4252 if (type != kLine_Segment) {
4253 return type;
4254 }
4255 if (segment.isHorizontal()) {
4256 return kHorizontalLine_Segment;
4257 }
4258 if (segment.isVertical()) {
4259 return kVerticalLine_Segment;
4260 }
4261 return kLine_Segment;
4262 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004263
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004264 bool startAfter(const Work& after) {
4265 fIndex = after.fIndex;
4266 return advance();
4267 }
4268
4269 SkScalar top() const {
4270 return bounds().fTop;
4271 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004272
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004273 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004274 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004275 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004276
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004277 SkScalar x() const {
4278 return bounds().fLeft;
4279 }
4280
4281 bool xFlipped() const {
4282 return x() != pts()[0].fX;
4283 }
4284
4285 SkScalar y() const {
4286 return bounds().fTop;
4287 }
4288
4289 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004290 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004291 }
4292
4293protected:
4294 Contour* fContour;
4295 SkPoint fCubic[4];
4296 int fIndex;
4297 int fLast;
4298};
4299
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004300#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004301static void debugShowLineIntersection(int pts, const Work& wt,
4302 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004303 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004304 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004305 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4306 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4307 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4308 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004309 return;
4310 }
4311 SkPoint wtOutPt, wnOutPt;
4312 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4313 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004314 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 +00004315 __FUNCTION__,
4316 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4317 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4318 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004319 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004320 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004321 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004322 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4323 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4324 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004325 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4326 }
4327 SkDebugf("\n");
4328}
4329
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004330static void debugShowQuadLineIntersection(int pts, const Work& wt,
4331 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4332 if (!pts) {
4333 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00004334 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004335 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4336 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004337 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004338 return;
4339 }
4340 SkPoint wtOutPt, wnOutPt;
4341 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4342 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4343 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4344 __FUNCTION__,
4345 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4346 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4347 wtOutPt.fX, wtOutPt.fY);
4348 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004349 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
4350 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004351 }
4352 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4353 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4354 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4355 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004356 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
4357 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004358 }
4359 SkDebugf("\n");
4360}
4361
caryclark@google.coma461ff02012-10-11 12:54:23 +00004362static void debugShowQuadIntersection(int pts, const Work& wt,
4363 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4364 if (!pts) {
4365 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
4366 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
4367 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00004368 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00004369 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004370 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00004371 return;
4372 }
4373 SkPoint wtOutPt, wnOutPt;
4374 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4375 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4376 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4377 __FUNCTION__,
4378 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4379 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4380 wtOutPt.fX, wtOutPt.fY);
4381 if (pts == 2) {
4382 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
4383 }
4384 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4385 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4386 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
4387 wnOutPt.fX, wnOutPt.fY);
4388 if (pts == 2) {
4389 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004390 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004391 SkDebugf("\n");
4392}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004393#else
4394static void debugShowLineIntersection(int , const Work& ,
4395 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004396}
caryclark@google.coma461ff02012-10-11 12:54:23 +00004397
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004398static void debugShowQuadLineIntersection(int , const Work& ,
4399 const Work& , const double [2], const double [2]) {
4400}
4401
caryclark@google.coma461ff02012-10-11 12:54:23 +00004402static void debugShowQuadIntersection(int , const Work& ,
4403 const Work& , const double [2], const double [2]) {
4404}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004405#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004406
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004407static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004408
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004409 if (test != next) {
4410 if (test->bounds().fBottom < next->bounds().fTop) {
4411 return false;
4412 }
4413 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
4414 return true;
4415 }
4416 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004417 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004418 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004419 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004420 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004421 Work wn;
4422 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004423 if (test == next && !wn.startAfter(wt)) {
4424 continue;
4425 }
4426 do {
4427 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
4428 continue;
4429 }
4430 int pts;
4431 Intersections ts;
4432 bool swap = false;
4433 switch (wt.segmentType()) {
4434 case Work::kHorizontalLine_Segment:
4435 swap = true;
4436 switch (wn.segmentType()) {
4437 case Work::kHorizontalLine_Segment:
4438 case Work::kVerticalLine_Segment:
4439 case Work::kLine_Segment: {
4440 pts = HLineIntersect(wn.pts(), wt.left(),
4441 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004442 debugShowLineIntersection(pts, wt, wn,
4443 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004444 break;
4445 }
4446 case Work::kQuad_Segment: {
4447 pts = HQuadIntersect(wn.pts(), wt.left(),
4448 wt.right(), wt.y(), wt.xFlipped(), ts);
4449 break;
4450 }
4451 case Work::kCubic_Segment: {
4452 pts = HCubicIntersect(wn.pts(), wt.left(),
4453 wt.right(), wt.y(), wt.xFlipped(), ts);
4454 break;
4455 }
4456 default:
4457 SkASSERT(0);
4458 }
4459 break;
4460 case Work::kVerticalLine_Segment:
4461 swap = true;
4462 switch (wn.segmentType()) {
4463 case Work::kHorizontalLine_Segment:
4464 case Work::kVerticalLine_Segment:
4465 case Work::kLine_Segment: {
4466 pts = VLineIntersect(wn.pts(), wt.top(),
4467 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004468 debugShowLineIntersection(pts, wt, wn,
4469 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004470 break;
4471 }
4472 case Work::kQuad_Segment: {
4473 pts = VQuadIntersect(wn.pts(), wt.top(),
4474 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4475 break;
4476 }
4477 case Work::kCubic_Segment: {
4478 pts = VCubicIntersect(wn.pts(), wt.top(),
4479 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4480 break;
4481 }
4482 default:
4483 SkASSERT(0);
4484 }
4485 break;
4486 case Work::kLine_Segment:
4487 switch (wn.segmentType()) {
4488 case Work::kHorizontalLine_Segment:
4489 pts = HLineIntersect(wt.pts(), wn.left(),
4490 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004491 debugShowLineIntersection(pts, wt, wn,
4492 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004493 break;
4494 case Work::kVerticalLine_Segment:
4495 pts = VLineIntersect(wt.pts(), wn.top(),
4496 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004497 debugShowLineIntersection(pts, wt, wn,
4498 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004499 break;
4500 case Work::kLine_Segment: {
4501 pts = LineIntersect(wt.pts(), wn.pts(), ts);
4502 debugShowLineIntersection(pts, wt, wn,
4503 ts.fT[1], ts.fT[0]);
4504 break;
4505 }
4506 case Work::kQuad_Segment: {
4507 swap = true;
4508 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004509 debugShowQuadLineIntersection(pts, wn, wt,
4510 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004511 break;
4512 }
4513 case Work::kCubic_Segment: {
4514 swap = true;
4515 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
4516 break;
4517 }
4518 default:
4519 SkASSERT(0);
4520 }
4521 break;
4522 case Work::kQuad_Segment:
4523 switch (wn.segmentType()) {
4524 case Work::kHorizontalLine_Segment:
4525 pts = HQuadIntersect(wt.pts(), wn.left(),
4526 wn.right(), wn.y(), wn.xFlipped(), ts);
4527 break;
4528 case Work::kVerticalLine_Segment:
4529 pts = VQuadIntersect(wt.pts(), wn.top(),
4530 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4531 break;
4532 case Work::kLine_Segment: {
4533 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004534 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004535 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004536 break;
4537 }
4538 case Work::kQuad_Segment: {
4539 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004540 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004541 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004542 break;
4543 }
4544 case Work::kCubic_Segment: {
4545 wt.promoteToCubic();
4546 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
4547 break;
4548 }
4549 default:
4550 SkASSERT(0);
4551 }
4552 break;
4553 case Work::kCubic_Segment:
4554 switch (wn.segmentType()) {
4555 case Work::kHorizontalLine_Segment:
4556 pts = HCubicIntersect(wt.pts(), wn.left(),
4557 wn.right(), wn.y(), wn.xFlipped(), ts);
4558 break;
4559 case Work::kVerticalLine_Segment:
4560 pts = VCubicIntersect(wt.pts(), wn.top(),
4561 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4562 break;
4563 case Work::kLine_Segment: {
4564 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
4565 break;
4566 }
4567 case Work::kQuad_Segment: {
4568 wn.promoteToCubic();
4569 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
4570 break;
4571 }
4572 case Work::kCubic_Segment: {
4573 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4574 break;
4575 }
4576 default:
4577 SkASSERT(0);
4578 }
4579 break;
4580 default:
4581 SkASSERT(0);
4582 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004583 if (!foundCommonContour && pts > 0) {
4584 test->addCross(next);
4585 next->addCross(test);
4586 foundCommonContour = true;
4587 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004588 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004589 if (pts == 2) {
4590 if (wn.segmentType() <= Work::kLine_Segment
4591 && wt.segmentType() <= Work::kLine_Segment) {
4592 wt.addCoincident(wn, ts, swap);
4593 continue;
4594 }
4595 if (wn.segmentType() == Work::kQuad_Segment
4596 && wt.segmentType() == Work::kQuad_Segment
4597 && ts.coincidentUsed() == 2) {
4598 wt.addCoincident(wn, ts, swap);
4599 continue;
4600 }
4601
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004602 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004603 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004604 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4605 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004606 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4607 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004608 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4609 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004610 }
4611 } while (wn.advance());
4612 } while (wt.advance());
4613 return true;
4614}
4615
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004616// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004617// see if coincidence is formed by clipping non-concident segments
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004618static void coincidenceCheck(SkTDArray<Contour*>& contourList, bool otherXor) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004619 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004620 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004621 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004622 contour->resolveCoincidence();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004623 }
4624 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4625 Contour* contour = contourList[cIndex];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004626 contour->findTooCloseToCall(otherXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004627 }
4628}
4629
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004630// project a ray from the top of the contour up and see if it hits anything
4631// note: when we compute line intersections, we keep track of whether
4632// two contours touch, so we need only look at contours not touching this one.
4633// OPTIMIZATION: sort contourList vertically to avoid linear walk
4634static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com31143cf2012-11-09 22:14:19 +00004635 const Segment* current, int index, int endIndex, bool opp) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004636 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004637 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004638 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00004639 const Segment* test = NULL;
4640 int tIndex;
4641 double tHit;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004642 for (int cTest = 0; cTest < contourCount; ++cTest) {
4643 Contour* contour = contourList[cTest];
caryclark@google.com31143cf2012-11-09 22:14:19 +00004644 if (contour->operand() ^ current->operand() != opp) {
4645 continue;
4646 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004647 if (basePt.fY < contour->bounds().fTop) {
4648 continue;
4649 }
4650 if (bestY > contour->bounds().fBottom) {
4651 continue;
4652 }
caryclark@google.com47580692012-07-23 12:14:49 +00004653 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
4654 if (next) {
4655 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004656 }
caryclark@google.com47580692012-07-23 12:14:49 +00004657 }
4658 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00004659 return 0;
4660 }
4661 int winding, windValue;
4662 // If the ray hit the end of a span, we need to construct the wheel of
4663 // angles to find the span closest to the ray -- even if there are just
4664 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004665 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004666 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00004667 SkTDArray<Angle> angles;
4668 int end = test->nextSpan(tIndex, 1);
4669 if (end < 0) {
4670 end = test->nextSpan(tIndex, -1);
4671 }
4672 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00004673 SkASSERT(angles.count() > 0);
4674 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
4675#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00004676 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00004677#endif
4678 return 0;
4679 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004680 test->buildAngles(tIndex, angles, false);
caryclark@google.com47580692012-07-23 12:14:49 +00004681 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00004682 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00004683 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004684 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00004685 // hour after 6 o'clock
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004686 (void) Segment::SortAngles(angles, sorted);
caryclark@google.com47580692012-07-23 12:14:49 +00004687#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004688 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00004689#endif
4690 // walk the sorted angle fan to find the lowest angle
4691 // above the base point. Currently, the first angle in the sorted array
4692 // is 12 noon or an earlier hour (the next counterclockwise)
4693 int count = sorted.count();
4694 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004695 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00004696 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004697 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00004698 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00004699 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004700 if (angle->unsortable()) {
4701 continue;
4702 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004703 if (baseMatches && angle->isHorizontal()) {
4704 continue;
4705 }
4706 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00004707 test = angle->segment();
4708 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
4709 const SkPoint* pts = test->pts();
4710 indexDx = pts[2].fX - pts[1].fX - indexDx;
4711 }
caryclark@google.com47580692012-07-23 12:14:49 +00004712 if (indexDx < 0) {
4713 left = index;
4714 } else if (indexDx > 0) {
4715 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00004716 int previous = index - 1;
4717 if (previous < 0) {
4718 previous = count - 1;
4719 }
4720 const Angle* prev = sorted[previous];
4721 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
4722#if DEBUG_SORT
4723 SkDebugf("%s use prev\n", __FUNCTION__);
4724#endif
4725 right = previous;
4726 }
caryclark@google.com47580692012-07-23 12:14:49 +00004727 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004728 } else {
4729 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004730 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004731 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004732 if (left < 0 && right < 0) {
4733 left = mid;
4734 }
caryclark@google.com47580692012-07-23 12:14:49 +00004735 SkASSERT(left >= 0 || right >= 0);
4736 if (left < 0) {
4737 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004738 } else if (left >= 0 && mid >= 0 && right >= 0
4739 && sorted[mid]->sign() == sorted[right]->sign()) {
4740 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004741 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004742 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004743 test = angle->segment();
4744 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004745 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004746 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004747#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004748 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4749 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004750#endif
4751 } else {
4752 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004753 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004754 windValue = test->windValue(tIndex);
4755#if DEBUG_WINDING
4756 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4757 windValue);
4758#endif
4759 }
4760 // see if a + change in T results in a +/- change in X (compute x'(T))
4761 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004762 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4763 const SkPoint* pts = test->pts();
4764 dx = pts[2].fX - pts[1].fX - dx;
4765 }
caryclark@google.com47580692012-07-23 12:14:49 +00004766#if DEBUG_WINDING
4767 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4768#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004769 SkASSERT(dx != 0);
4770 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004771 winding += dx > 0 ? -windValue : windValue;
4772#if DEBUG_WINDING
4773 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4774#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004775 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004776 return winding;
4777}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004778
caryclark@google.com24bec792012-08-20 12:43:57 +00004779static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4780 int contourCount = contourList.count();
4781 Segment* result;
4782 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4783 Contour* contour = contourList[cIndex];
4784 result = contour->undoneSegment(start, end);
4785 if (result) {
4786 return result;
4787 }
4788 }
4789 return NULL;
4790}
4791
4792
4793
caryclark@google.com31143cf2012-11-09 22:14:19 +00004794static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004795 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004796 Span* span;
4797 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004798 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4799 Segment* segment = backPtr.fOther;
4800 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004801 SkTDArray<Angle> angles;
4802 int done = 0;
4803 if (segment->activeAngle(tIndex, done, angles)) {
4804 Angle* last = angles.end() - 1;
4805 tIndex = last->start();
4806 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00004807 #if TRY_ROTATE
4808 *chase.insert(0) = span;
4809 #else
4810 *chase.append() = span;
4811 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004812 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004813 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004814 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004815 continue;
4816 }
4817 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004818 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004819#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004820 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004821#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004822 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004823 continue;
4824 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004825 // find first angle, initialize winding to computed fWindSum
4826 int firstIndex = -1;
4827 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004828 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004829 do {
4830 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004831 segment = angle->segment();
4832 winding = segment->windSum(angle);
4833 } while (winding == SK_MinS32);
4834 int spanWinding = segment->spanSign(angle->start(), angle->end());
4835 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00004836 SkDebugf("%s winding=%d spanWinding=%d\n",
4837 __FUNCTION__, winding, spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00004838 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00004839 // turn span winding into contour winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004840 if (spanWinding * winding < 0) {
4841 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004842 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004843 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004844 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004845 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004846 // we care about first sign and whether wind sum indicates this
4847 // edge is inside or outside. Maybe need to pass span winding
4848 // or first winding or something into this function?
4849 // advance to first undone angle, then return it and winding
4850 // (to set whether edges are active or not)
4851 int nextIndex = firstIndex + 1;
4852 int angleCount = sorted.count();
4853 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004854 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00004855 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00004856 do {
4857 SkASSERT(nextIndex != firstIndex);
4858 if (nextIndex == angleCount) {
4859 nextIndex = 0;
4860 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004861 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00004862 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004863 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00004864 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004865 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00004866 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
4867 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004868 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004869 tIndex = angle->start();
4870 endIndex = angle->end();
4871 int lesser = SkMin32(tIndex, endIndex);
4872 const Span& nextSpan = segment->span(lesser);
4873 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004874#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00004875 // FIXME: this be wrong. assign startWinding if edge is in
4876 // same direction. If the direction is opposite, winding to
4877 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00004878 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004879 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004880 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004881 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004882#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004883 break;
4884 }
4885 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004886 #if TRY_ROTATE
4887 *chase.insert(0) = span;
4888 #else
4889 *chase.append() = span;
4890 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004891 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004892 }
4893 return NULL;
4894}
4895
caryclark@google.com027de222012-07-12 12:52:50 +00004896#if DEBUG_ACTIVE_SPANS
4897static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004898 int index;
4899 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00004900 contourList[index]->debugShowActiveSpans();
4901 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004902 for (index = 0; index < contourList.count(); ++ index) {
4903 contourList[index]->validateActiveSpans();
4904 }
caryclark@google.com027de222012-07-12 12:52:50 +00004905}
4906#endif
4907
caryclark@google.com27c449a2012-07-27 18:26:38 +00004908static bool windingIsActive(int winding, int spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004909 // FIXME: !spanWinding test must be superflorous, true?
caryclark@google.com27c449a2012-07-27 18:26:38 +00004910 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
4911 && (!winding || !spanWinding || winding == -spanWinding);
4912}
4913
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004914static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comf839c032012-10-26 21:03:50 +00004915 int& endIndex, SkPoint& topLeft) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004916 Segment* result;
4917 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00004918 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004919 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00004920 Segment* topStart = NULL;
4921 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4922 Contour* contour = contourList[cIndex];
4923 const Bounds& bounds = contour->bounds();
4924 if (bounds.fBottom < topLeft.fY) {
4925 continue;
4926 }
4927 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
4928 continue;
4929 }
4930 Segment* test = contour->topSortableSegment(topLeft, bestXY);
4931 if (test) {
4932 topStart = test;
4933 }
4934 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004935 if (!topStart) {
4936 return NULL;
4937 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004938 topLeft = bestXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004939 result = topStart->findTop(index, endIndex);
4940 } while (!result);
4941 return result;
4942}
caryclark@google.com31143cf2012-11-09 22:14:19 +00004943
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004944static int updateWindings(const Segment* current, int index, int endIndex, int& spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004945 int lesser = SkMin32(index, endIndex);
4946 spanWinding = current->spanSign(index, endIndex);
4947 int winding = current->windSum(lesser);
4948 bool inner = useInnerWinding(winding - spanWinding, winding);
4949#if DEBUG_WINDING
4950 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
4951 " inner=%d result=%d\n",
4952 __FUNCTION__, current->debugID(), current->t(lesser),
4953 spanWinding, winding, SkSign32(index - endIndex),
4954 useInnerWinding(winding - spanWinding, winding),
4955 inner ? winding - spanWinding : winding);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004956#endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00004957 if (inner) {
4958 winding -= spanWinding;
4959 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004960 return winding;
4961}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004962
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004963// Each segment may have an inside or an outside. Segments contained within
4964// winding may have insides on either side, and form a contour that should be
4965// ignored. Segments that are coincident with opposing direction segments may
4966// have outsides on either side, and should also disappear.
4967// 'Normal' segments will have one inside and one outside. Subsequent connections
4968// when winding should follow the intersection direction. If more than one edge
4969// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004970 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00004971 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004972// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00004973static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004974 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004975 bool unsortable = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00004976 bool closable = true;
4977 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com15fa1382012-05-07 20:49:36 +00004978 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00004979 int index, endIndex;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004980 // iterates while top is unsortable
caryclark@google.comf839c032012-10-26 21:03:50 +00004981 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004982 if (!current) {
4983 break;
4984 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004985 int contourWinding;
4986 if (firstContour) {
4987 contourWinding = 0;
4988 firstContour = false;
4989 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004990 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004991 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00004992 if (sumWinding == SK_MinS32) {
4993 sumWinding = current->computeSum(index, endIndex);
4994 }
4995 if (sumWinding == SK_MinS32) {
4996 contourWinding = innerContourCheck(contourList, current,
caryclark@google.com31143cf2012-11-09 22:14:19 +00004997 index, endIndex, false);
caryclark@google.com200c2112012-08-03 15:05:04 +00004998 } else {
4999 contourWinding = sumWinding;
5000 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00005001 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
5002 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00005003 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005004 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00005005#if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005006 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n",
5007 __FUNCTION__, sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00005008 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00005009#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005010 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005011#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005012 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005013 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
5014#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005015 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005016 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005017 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005018 // FIXME: needs work. While it works in limited situations, it does
5019 // not always compute winding correctly. Active should be removed and instead
5020 // the initial winding should be correctly passed in so that if the
5021 // inner contour is wound the same way, it never finds an accumulated
5022 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00005023 // other than zero when resolving sorted angles.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005024 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005025 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005026 bool active = windingIsActive(winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005027 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00005028 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
5029 __FUNCTION__, active ? "true" : "false",
5030 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005031 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005032 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005033 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005034 int nextStart = index;
5035 int nextEnd = endIndex;
5036 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005037 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005038 if (!next) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005039 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00005040 && current->verb() != SkPath::kLine_Verb
5041 && !simple.isClosed()) {
5042 current->addCurveTo(index, endIndex, simple, true);
5043 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005044 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005045 break;
5046 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005047 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005048 current = next;
5049 index = nextStart;
5050 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005051 } while (!simple.isClosed()
5052 && ((active && !unsortable) || !current->done()));
5053 if (active) {
5054 if (!simple.isClosed()) {
5055 SkASSERT(unsortable);
5056 int min = SkMin32(index, endIndex);
5057 if (!current->done(min)) {
5058 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.com31143cf2012-11-09 22:14:19 +00005059 current->markDone(SkMin32(index, endIndex),
5060 winding ? winding : spanWinding);
caryclark@google.comf839c032012-10-26 21:03:50 +00005061 }
5062 closable = false;
5063 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005064 simple.close();
5065 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005066 current = findChase(chaseArray, index, endIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005067 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00005068 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005069 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005070 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005071 break;
5072 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005073 winding = updateWindings(current, index, endIndex, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005074 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005075 } while (true);
caryclark@google.comf839c032012-10-26 21:03:50 +00005076 return closable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005077}
5078
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005079// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005080static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005081 Segment* current;
5082 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005083 bool unsortable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00005084 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005085 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005086 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005087 int nextStart = start;
5088 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005089 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00005090 if (!next) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005091 if (simple.hasMove()
5092 && current->verb() != SkPath::kLine_Verb
5093 && !simple.isClosed()) {
5094 current->addCurveTo(start, end, simple, true);
5095 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005096 }
caryclark@google.com24bec792012-08-20 12:43:57 +00005097 break;
5098 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005099 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00005100 current = next;
5101 start = nextStart;
5102 end = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005103 } while (!simple.isClosed());
5104 // FIXME: add unsortable test
5105 if (simple.hasMove()) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005106 simple.close();
5107 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005108 #if DEBUG_ACTIVE_SPANS
5109 debugShowActiveSpans(contourList);
5110 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005111 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005112 return !unsortable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005113}
5114
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005115static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5116 int contourCount = contourList.count();
5117 for (int cTest = 0; cTest < contourCount; ++cTest) {
5118 Contour* contour = contourList[cTest];
5119 contour->fixOtherTIndex();
5120 }
5121}
5122
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005123static void sortSegments(SkTDArray<Contour*>& contourList) {
5124 int contourCount = contourList.count();
5125 for (int cTest = 0; cTest < contourCount; ++cTest) {
5126 Contour* contour = contourList[cTest];
5127 contour->sortSegments();
5128 }
5129}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005130
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005131static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005132 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005133 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005134 if (count == 0) {
5135 return;
5136 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005137 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005138 *list.append() = &contours[index];
5139 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005140 QSort<Contour>(list.begin(), list.end() - 1);
5141}
5142
caryclark@google.comf839c032012-10-26 21:03:50 +00005143static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005144 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005145}
5146
caryclark@google.comf839c032012-10-26 21:03:50 +00005147 /*
5148 check start and end of each contour
5149 if not the same, record them
5150 match them up
5151 connect closest
5152 reassemble contour pieces into new path
5153 */
5154static void assemble(const PathWrapper& path, PathWrapper& simple) {
5155#if DEBUG_PATH_CONSTRUCTION
5156 SkDebugf("%s\n", __FUNCTION__);
5157#endif
5158 SkTArray<Contour> contours;
5159 EdgeBuilder builder(path, contours);
5160 builder.finish();
5161 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005162 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005163 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00005164 for (outer = 0; outer < count; ++outer) {
5165 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005166 const SkPoint& eStart = eContour.start();
5167 const SkPoint& eEnd = eContour.end();
5168 if (approximatelyEqual(eStart, eEnd)) {
5169 eContour.toPath(simple);
5170 continue;
5171 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005172 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005173 }
5174 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005175 if (count == 0) {
5176 return;
5177 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005178 SkTDArray<int> sLink, eLink;
5179 sLink.setCount(count);
5180 eLink.setCount(count);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005181 SkTDArray<double> sBest, eBest;
5182 sBest.setCount(count);
5183 eBest.setCount(count);
caryclark@google.comf839c032012-10-26 21:03:50 +00005184 int rIndex;
5185 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005186 outer = runs[rIndex];
5187 const Contour& oContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005188 const SkPoint& oStart = oContour.start();
5189 const SkPoint& oEnd = oContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005190 double dx = oEnd.fX - oStart.fX;
5191 double dy = oEnd.fY - oStart.fY;
5192 double dist = dx * dx + dy * dy;
5193 sBest[rIndex] = eBest[rIndex] = dist;
5194 sLink[rIndex] = eLink[rIndex] = rIndex;
5195 }
5196 for (rIndex = 0; rIndex < count - 1; ++rIndex) {
5197 outer = runs[rIndex];
5198 const Contour& oContour = contours[outer];
5199 const SkPoint& oStart = oContour.start();
5200 const SkPoint& oEnd = oContour.end();
5201 double bestStartDist = sBest[rIndex];
5202 double bestEndDist = eBest[rIndex];
5203 for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) {
5204 int inner = runs[iIndex];
5205 const Contour& iContour = contours[inner];
caryclark@google.comf839c032012-10-26 21:03:50 +00005206 const SkPoint& iStart = iContour.start();
5207 const SkPoint& iEnd = iContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005208 double dx = iStart.fX - oStart.fX;
5209 double dy = iStart.fY - oStart.fY;
5210 double dist = dx * dx + dy * dy;
5211 if (bestStartDist > dist) {
5212 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005213 sLink[rIndex] = ~iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005214 sLink[iIndex] = ~rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005215 }
5216 dx = iEnd.fX - oStart.fX;
5217 dy = iEnd.fY - oStart.fY;
5218 dist = dx * dx + dy * dy;
5219 if (bestStartDist > dist) {
5220 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005221 sLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005222 eLink[iIndex] = rIndex;
5223 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005224 dx = iStart.fX - oEnd.fX;
5225 dy = iStart.fY - oEnd.fY;
5226 dist = dx * dx + dy * dy;
5227 if (bestEndDist > dist) {
5228 bestEndDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005229 sLink[iIndex] = rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005230 eLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005231 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005232 dx = iEnd.fX - oEnd.fX;
5233 dy = iEnd.fY - oEnd.fY;
5234 dist = dx * dx + dy * dy;
5235 if (bestEndDist > dist) {
5236 bestEndDist = dist;
5237 eLink[iIndex] = ~rIndex;
5238 eLink[rIndex] = ~iIndex;
5239 }
5240 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005241 }
5242 rIndex = 0;
5243 bool forward = true;
5244 bool first = true;
5245 const SkPoint* startPtr;
5246 int sIndex = sLink[rIndex];
caryclark@google.com0b7da432012-10-31 19:00:20 +00005247 SkASSERT(sIndex != INT_MAX);
5248 sLink[rIndex] = INT_MAX;
5249 int eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005250 if (sIndex < 0) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005251 eIndex = sLink[~sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005252 sLink[~sIndex] = INT_MAX;
5253 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005254 eIndex = eLink[sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005255 eLink[sIndex] = INT_MAX;
5256 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005257 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005258 do {
5259 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005260 outer = runs[rIndex];
5261 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005262 if (first) {
5263 startPtr = &contour.start();
5264 first = false;
5265 simple.deferredMove(startPtr[0]);
5266 }
5267 const SkPoint* endPtr;
5268 if (forward) {
5269 contour.toPartialForward(simple);
5270 endPtr = &contour.end();
5271 } else {
5272 contour.toPartialBackward(simple);
5273 endPtr = &contour.start();
5274 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005275 if (sIndex == eIndex) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005276 simple.close();
5277 first = forward = true;
5278 break;
5279 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005280 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005281 eIndex = eLink[rIndex];
5282 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005283 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005284 if (eIndex >= 0) {
5285 SkASSERT(sLink[eIndex] == rIndex);
5286 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005287 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005288 SkASSERT(eLink[~eIndex] == ~rIndex);
5289 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005290 }
5291 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005292 eIndex = sLink[rIndex];
5293 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005294 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005295 if (eIndex >= 0) {
5296 SkASSERT(eLink[eIndex] == rIndex);
5297 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005298 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005299 SkASSERT(sLink[~eIndex] == ~rIndex);
5300 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005301 }
5302 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005303 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005304 if (rIndex < 0) {
5305 forward ^= 1;
5306 rIndex = ~rIndex;
5307 }
5308 } while (true);
5309 for (rIndex = 0; rIndex < count; ++rIndex) {
5310 if (sLink[rIndex] != INT_MAX) {
5311 break;
5312 }
5313 }
5314 } while (rIndex < count);
5315 SkASSERT(first);
5316}
5317
5318void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005319 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00005320 result.reset();
5321 result.setFillType(SkPath::kEvenOdd_FillType);
5322 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005323
5324 // turn path into list of segments
5325 SkTArray<Contour> contours;
5326 // FIXME: add self-intersecting cubics' T values to segment
5327 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00005328 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005329 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005330 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005331 Contour** currentPtr = contourList.begin();
5332 if (!currentPtr) {
5333 return;
5334 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005335 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005336 // find all intersections between segments
5337 do {
5338 Contour** nextPtr = currentPtr;
5339 Contour* current = *currentPtr++;
5340 Contour* next;
5341 do {
5342 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005343 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005344 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005345 // eat through coincident edges
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005346 coincidenceCheck(contourList, false);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00005347 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005348 sortSegments(contourList);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005349#if DEBUG_ACTIVE_SPANS
5350 debugShowActiveSpans(contourList);
5351#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005352 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005353 if (builder.xorMask() == kWinding_Mask
5354 ? !bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00005355 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005356 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00005357 SkPath temp;
5358 temp.setFillType(SkPath::kEvenOdd_FillType);
5359 PathWrapper assembled(temp);
5360 assemble(simple, assembled);
5361 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00005362 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005363}
5364