blob: 78c36f65107902ea92055e6216553c2d0595cbbc [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.com3350c3c2012-08-24 15:24:36 +000028#define HIGH_DEF_ANGLES 1
29
30#if HIGH_DEF_ANGLES
31typedef double AngleValue;
32#else
33typedef SkScalar AngleValue;
34#endif
35
caryclark@google.com47580692012-07-23 12:14:49 +000036#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa0588f2012-04-26 21:01:06 +000037
caryclark@google.com32546db2012-08-31 20:55:07 +000038#if 1 // set to 1 for multiple thread -- no debugging
caryclark@google.com47580692012-07-23 12:14:49 +000039
40const bool gRunTestsInOneThread = false;
41
42#define DEBUG_ACTIVE_SPANS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000043#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000044#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000045#define DEBUG_ANGLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000046#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000047#define DEBUG_CROSS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000048#define DEBUG_DUMP 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000049#define DEBUG_MARK_DONE 0
caryclark@google.com47580692012-07-23 12:14:49 +000050#define DEBUG_PATH_CONSTRUCTION 0
51#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000052#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000053#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000054
55#else
56
caryclark@google.com47580692012-07-23 12:14:49 +000057const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000058
caryclark@google.comafe56de2012-07-24 18:11:03 +000059#define DEBUG_ACTIVE_SPANS 1
caryclark@google.comc899ad92012-08-23 15:24:42 +000060#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com24bec792012-08-20 12:43:57 +000061#define DEBUG_ADD_T_PAIR 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000062#define DEBUG_ANGLE 1
63#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000064#define DEBUG_CROSS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000065#define DEBUG_DUMP 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000066#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000067#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000068#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000069#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000070#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000071
72#endif
73
caryclark@google.com534aa5b2012-08-02 20:08:21 +000074#if (DEBUG_ACTIVE_SPANS || DEBUG_CONCIDENT || DEBUG_SORT) && !DEBUG_DUMP
caryclark@google.com027de222012-07-12 12:52:50 +000075#undef DEBUG_DUMP
76#define DEBUG_DUMP 1
77#endif
78
caryclark@google.comfa0588f2012-04-26 21:01:06 +000079#if DEBUG_DUMP
80static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000081// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000082static int gContourID;
83static int gSegmentID;
84#endif
85
caryclark@google.com8dcf1142012-07-02 20:27:02 +000086#ifndef DEBUG_TEST
87#define DEBUG_TEST 0
88#endif
89
caryclark@google.com32546db2012-08-31 20:55:07 +000090#define MAKE_CONST_LINE(line, pts) \
91 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
92#define MAKE_CONST_QUAD(quad, pts) \
93 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
94 {pts[2].fX, pts[2].fY}}
95#define MAKE_CONST_CUBIC(cubic, pts) \
96 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
97 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
98
caryclark@google.comfa0588f2012-04-26 21:01:06 +000099static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
100 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000101 MAKE_CONST_LINE(aLine, a);
102 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000103 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
104}
105
106static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
107 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000108 MAKE_CONST_QUAD(aQuad, a);
109 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000110 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000111}
112
caryclark@google.com32546db2012-08-31 20:55:07 +0000113static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000114 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000115 MAKE_CONST_CUBIC(aCubic, a);
116 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000117 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
118}
119
120static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
121 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000122 MAKE_CONST_QUAD(aQuad, a);
123 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000124#define TRY_QUARTIC_SOLUTION 1
125#if TRY_QUARTIC_SOLUTION
126 intersect2(aQuad, bQuad, intersections);
127#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000128 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000129#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000130 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000131}
132
133static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
134 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000135 MAKE_CONST_CUBIC(aCubic, a);
136 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000137 intersect(aCubic, bCubic, intersections);
138 return intersections.fUsed;
139}
140
141static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
142 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000143 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000144 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
145}
146
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000147static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
148 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000149 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000150 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
151}
152
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000153static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
154 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000155 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000156 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
157}
158
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000159static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
160 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000161 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000162 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
163}
164
165static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
166 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000167 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000168 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
169}
170
171static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
172 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000173 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000174 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000175}
176
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000177static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
178 SkScalar , SkScalar , bool , Intersections& ) = {
179 NULL,
180 VLineIntersect,
181 VQuadIntersect,
182 VCubicIntersect
183};
184
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000185static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000186 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000187 double x, y;
188 xy_at_t(line, t, x, y);
189 out->fX = SkDoubleToScalar(x);
190 out->fY = SkDoubleToScalar(y);
191}
192
193static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000194 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000195 double x, y;
196 xy_at_t(quad, t, x, y);
197 out->fX = SkDoubleToScalar(x);
198 out->fY = SkDoubleToScalar(y);
199}
200
201static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000202 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000203 double x, y;
204 xy_at_t(cubic, t, x, y);
205 out->fX = SkDoubleToScalar(x);
206 out->fY = SkDoubleToScalar(y);
207}
208
209static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
210 NULL,
211 LineXYAtT,
212 QuadXYAtT,
213 CubicXYAtT
214};
215
216static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000217 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000218 double x;
219 xy_at_t(aLine, t, x, *(double*) 0);
220 return SkDoubleToScalar(x);
221}
222
223static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000224 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000225 double x;
226 xy_at_t(quad, t, x, *(double*) 0);
227 return SkDoubleToScalar(x);
228}
229
230static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000231 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000232 double x;
233 xy_at_t(cubic, t, x, *(double*) 0);
234 return SkDoubleToScalar(x);
235}
236
237static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
238 NULL,
239 LineXAtT,
240 QuadXAtT,
241 CubicXAtT
242};
243
244static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000245 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000246 double y;
247 xy_at_t(aLine, t, *(double*) 0, y);
248 return SkDoubleToScalar(y);
249}
250
251static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000252 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000253 double y;
254 xy_at_t(quad, t, *(double*) 0, y);
255 return SkDoubleToScalar(y);
256}
257
258static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000259 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000260 double y;
261 xy_at_t(cubic, t, *(double*) 0, y);
262 return SkDoubleToScalar(y);
263}
264
265static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
266 NULL,
267 LineYAtT,
268 QuadYAtT,
269 CubicYAtT
270};
271
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000272static SkScalar LineDXAtT(const SkPoint a[2], double ) {
273 return a[1].fX - a[0].fX;
274}
275
276static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000277 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000278 double x;
279 dxdy_at_t(quad, t, x, *(double*) 0);
280 return SkDoubleToScalar(x);
281}
282
283static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000284 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000285 double x;
286 dxdy_at_t(cubic, t, x, *(double*) 0);
287 return SkDoubleToScalar(x);
288}
289
290static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
291 NULL,
292 LineDXAtT,
293 QuadDXAtT,
294 CubicDXAtT
295};
296
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000297static void LineSubDivide(const SkPoint a[2], double startT, double endT,
298 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000299 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000300 _Line dst;
301 sub_divide(aLine, startT, endT, dst);
302 sub[0].fX = SkDoubleToScalar(dst[0].x);
303 sub[0].fY = SkDoubleToScalar(dst[0].y);
304 sub[1].fX = SkDoubleToScalar(dst[1].x);
305 sub[1].fY = SkDoubleToScalar(dst[1].y);
306}
307
308static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
309 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000310 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000311 Quadratic dst;
312 sub_divide(aQuad, startT, endT, dst);
313 sub[0].fX = SkDoubleToScalar(dst[0].x);
314 sub[0].fY = SkDoubleToScalar(dst[0].y);
315 sub[1].fX = SkDoubleToScalar(dst[1].x);
316 sub[1].fY = SkDoubleToScalar(dst[1].y);
317 sub[2].fX = SkDoubleToScalar(dst[2].x);
318 sub[2].fY = SkDoubleToScalar(dst[2].y);
319}
320
321static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
322 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000323 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000324 Cubic dst;
325 sub_divide(aCubic, startT, endT, dst);
326 sub[0].fX = SkDoubleToScalar(dst[0].x);
327 sub[0].fY = SkDoubleToScalar(dst[0].y);
328 sub[1].fX = SkDoubleToScalar(dst[1].x);
329 sub[1].fY = SkDoubleToScalar(dst[1].y);
330 sub[2].fX = SkDoubleToScalar(dst[2].x);
331 sub[2].fY = SkDoubleToScalar(dst[2].y);
332 sub[3].fX = SkDoubleToScalar(dst[3].x);
333 sub[3].fY = SkDoubleToScalar(dst[3].y);
334}
335
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000336static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
337 SkPoint []) = {
338 NULL,
339 LineSubDivide,
340 QuadSubDivide,
341 CubicSubDivide
342};
343
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000344static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000345 _Line sub) {
346 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000347 _Line dst;
348 sub_divide(aLine, startT, endT, dst);
349 sub[0] = dst[0];
350 sub[1] = dst[1];
351}
352
353static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000354 Quadratic sub) {
355 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000356 Quadratic dst;
357 sub_divide(aQuad, startT, endT, dst);
358 sub[0] = dst[0];
359 sub[1] = dst[1];
360 sub[2] = dst[2];
361}
362
363static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000364 Cubic sub) {
365 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000366 Cubic dst;
367 sub_divide(aCubic, startT, endT, dst);
368 sub[0] = dst[0];
369 sub[1] = dst[1];
370 sub[2] = dst[2];
371 sub[3] = dst[3];
372}
373
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000374#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000375static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
376 SkRect& bounds) {
377 SkPoint dst[3];
378 QuadSubDivide(a, startT, endT, dst);
379 bounds.fLeft = bounds.fRight = dst[0].fX;
380 bounds.fTop = bounds.fBottom = dst[0].fY;
381 for (int index = 1; index < 3; ++index) {
382 bounds.growToInclude(dst[index].fX, dst[index].fY);
383 }
384}
385
386static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
387 SkRect& bounds) {
388 SkPoint dst[4];
389 CubicSubDivide(a, startT, endT, dst);
390 bounds.fLeft = bounds.fRight = dst[0].fX;
391 bounds.fTop = bounds.fBottom = dst[0].fY;
392 for (int index = 1; index < 4; ++index) {
393 bounds.growToInclude(dst[index].fX, dst[index].fY);
394 }
395}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000396#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000397
caryclark@google.com15fa1382012-05-07 20:49:36 +0000398static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000399 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000400 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000401 Quadratic dst;
402 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000403 if (order == 2) { // quad became line
404 for (int index = 0; index < order; ++index) {
405 SkPoint* pt = reducePts.append();
406 pt->fX = SkDoubleToScalar(dst[index].x);
407 pt->fY = SkDoubleToScalar(dst[index].y);
408 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000409 }
410 return (SkPath::Verb) (order - 1);
411}
412
413static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
414 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000415 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000416 Cubic dst;
417 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000418 if (order == 2 || order == 3) { // cubic became line or quad
419 for (int index = 0; index < order; ++index) {
420 SkPoint* pt = reducePts.append();
421 pt->fX = SkDoubleToScalar(dst[index].x);
422 pt->fY = SkDoubleToScalar(dst[index].y);
423 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000424 }
425 return (SkPath::Verb) (order - 1);
426}
427
caryclark@google.com15fa1382012-05-07 20:49:36 +0000428static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000429 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000430 return isLinear(aQuad, 0, 2);
431}
432
433static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000434 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000435 return isLinear(aCubic, 0, 3);
436}
437
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000438static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000439 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000440 double x[2];
441 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000442 xy_at_t(aLine, endT, x[1], *(double*) 0);
443 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000444}
445
446static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000447 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000448 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000449}
450
451static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000452 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000453 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000454}
455
456static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
457 NULL,
458 LineLeftMost,
459 QuadLeftMost,
460 CubicLeftMost
461};
462
caryclark@google.com235f56a2012-09-14 14:19:30 +0000463static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
464 Intersections& intersections) {
465 MAKE_CONST_QUAD(aQuad, a);
466 MAKE_CONST_LINE(bLine, b);
467 return intersectRay(aQuad, bLine, intersections);
468}
469
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000470class Segment;
471
caryclark@google.com15fa1382012-05-07 20:49:36 +0000472// sorting angles
473// given angles of {dx dy ddx ddy dddx dddy} sort them
474class Angle {
475public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000476 // FIXME: this is bogus for quads and cubics
477 // if the quads and cubics' line from end pt to ctrl pt are coincident,
478 // there's no obvious way to determine the curve ordering from the
479 // derivatives alone. In particular, if one quadratic's coincident tangent
480 // is longer than the other curve, the final control point can place the
481 // longer curve on either side of the shorter one.
482 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
483 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000484
caryclark@google.com32546db2012-08-31 20:55:07 +0000485 // start here
486 /*(
487 for quads and cubics, set up a parameterized line (e.g. LineParameters )
488 for points [0] to [1]. See if point [2] is on that line, or on one side
489 or the other. If it both quads' end points are on the same side, choose
490 the shorter tangent. If the tangents are equal, choose the better second
491 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000492
caryclark@google.com32546db2012-08-31 20:55:07 +0000493 maybe I set up LineParameters lazily
494 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000495 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000496 double y = dy();
497 double ry = rh.dy();
498 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
499 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000500 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000501 double x = dx();
502 double rx = rh.dx();
503 if (y == 0 && ry == 0 && x * rx < 0) {
504 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000505 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000506 AngleValue cmp = x * ry - rx * y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000507 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000508 return cmp < 0;
509 }
caryclark@google.com32546db2012-08-31 20:55:07 +0000510 // at this point, the initial tangent line is coincident
511 #if !HIGH_DEF_ANGLES // old way
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000512 AngleValue dy = approximately_pin(fDy + fDDy);
513 AngleValue rdy = approximately_pin(rh.fDy + rh.fDDy);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000514 if (dy * rdy < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000515 return dy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000516 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000517 AngleValue dx = approximately_pin(fDx + fDDx);
518 AngleValue rdx = approximately_pin(rh.fDx + rh.fDDx);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000519 if (dy == 0 && rdy == 0 && dx * rdx < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000520 return dx < rdx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000521 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000522 cmp = dx * rdy - rdx * dy;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000523 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000524 return cmp < 0;
525 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000526 dy = approximately_pin(dy + fDDDy);
527 rdy = approximately_pin(rdy + rh.fDDDy);
528 if (dy * rdy < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000529 return dy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000530 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000531 dx = approximately_pin(dx + fDDDx);
532 rdx = approximately_pin(rdx + rh.fDDDx);
533 if (dy == 0 && rdy == 0 && dx * rdx < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000534 return dx < rdx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000535 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000536 return dx * rdy < rdx * dy;
caryclark@google.com32546db2012-08-31 20:55:07 +0000537 #else // new way
538 if (fSide * rh.fSide <= 0) {
539 SkASSERT(fSide != rh.fSide);
540 return fSide < rh.fSide;
541 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000542 #if 0 // the following code is a bust. In particular, tangent length doesn't provide a sort
543 if (y != ry) {
544 return (fabs(y) < fabs(ry)) ^ (fSide > 0);
caryclark@google.com32546db2012-08-31 20:55:07 +0000545 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000546 if (x != rx) {
547 return (fabs(x) < fabs(rx)) ^ (fSide > 0);
caryclark@google.com32546db2012-08-31 20:55:07 +0000548 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000549 // sort by second tangent angle
550 cmp = fD2x * rh.fD2y - rh.fD2x * fD2y;
551 if (!approximately_zero(cmp)) {
552 return cmp < 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000553 }
554 SkASSERT(0); // add code for cubic case
caryclark@google.com235f56a2012-09-14 14:19:30 +0000555 #else
556 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
557 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
558 // FIXME: until I can think of something better, project a ray perpendicular from the
559 // end of the shorter tangent through both curves and use the resulting angle to sort
560 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
561 double len = fTangent1.normalSquared();
562 double rlen = rh.fTangent1.normalSquared();
563 SkPoint ray[2];
564 const Quadratic& q = len < rlen ? fQ : rh.fQ;
565 ray[0].fX = SkDoubleToScalar(q[1].x);
566 ray[0].fY = SkDoubleToScalar(q[1].y);
567 ray[1].fX = SkDoubleToScalar((q[0].x + q[2].x) / 2);
568 ray[1].fY = SkDoubleToScalar((q[0].y + q[2].y) / 2);
569 Intersections i, ri;
570 int roots = QuadRayIntersect(fPts, ray, i);
571 SkASSERT(roots > 0);
572 int rroots = QuadRayIntersect(rh.fPts, ray, ri);
573 SkASSERT(rroots > 0);
574 SkPoint loc;
575 SkScalar best = SK_ScalarInfinity;
576 SkScalar dx, dy, dist;
577 int index;
578 for (index = 0; index < roots; ++index) {
579 QuadXYAtT(fPts, i.fT[0][index], &loc);
580 dx = loc.fX - ray[0].fX;
581 dy = loc.fY - ray[0].fY;
582 dist = dx * dx + dy * dy;
583 if (best > dist) {
584 best = dist;
585 }
586 }
587 for (index = 0; index < rroots; ++index) {
588 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
589 dx = loc.fX - ray[0].fX;
590 dy = loc.fY - ray[0].fY;
591 dist = dx * dx + dy * dy;
592 if (best > dist) {
593 return fSide < 0;
594 }
595 }
596 return fSide > 0;
597 #endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000598 #endif
caryclark@google.com15fa1382012-05-07 20:49:36 +0000599 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000600
caryclark@google.com47580692012-07-23 12:14:49 +0000601 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000602#if HIGH_DEF_ANGLES
603 return fTangent1.dx();
604#else
caryclark@google.com47580692012-07-23 12:14:49 +0000605 return fDx;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000606#endif
caryclark@google.com47580692012-07-23 12:14:49 +0000607 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000608
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000609 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000610#if HIGH_DEF_ANGLES
611 return fTangent1.dy();
612#else
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000613 return fDy;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000614#endif
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000615 }
616
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000617 int end() const {
618 return fEnd;
619 }
620
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000621 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000622#if HIGH_DEF_ANGLES
623 return dy() == 0 && fVerb == SkPath::kLine_Verb;
624#else
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000625 return fDy == 0 && fDDy == 0 && fDDDy == 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000626#endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000627 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000628
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000629 // high precision version
630#if HIGH_DEF_ANGLES
631 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
632 int start, int end, double startT, double endT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000633 fSegment = segment;
634 fStart = start;
635 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000636 fPts = orig;
637 fVerb = verb;
caryclark@google.com32546db2012-08-31 20:55:07 +0000638 switch (verb) {
639 case SkPath::kLine_Verb:
640 _Line l;
641 LineSubDivideHD(orig, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000642 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
643 fTangent1.lineEndPoints(l);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000644 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000645 break;
646 case SkPath::kQuad_Verb:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000647 QuadSubDivideHD(orig, startT, endT, fQ);
648 fTangent1.quadEndPoints(fQ, 0, 1);
649 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000650 break;
651 case SkPath::kCubic_Verb:
652 Cubic c;
653 CubicSubDivideHD(orig, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000654 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000655 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000656 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000657 default:
658 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000659 }
caryclark@google.com32546db2012-08-31 20:55:07 +0000660 // OPTIMIZATION: don't need these, access fTangent1 directly
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000661 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000662
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000663#else
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000664 // since all angles share a point, this needs to know which point
665 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
666 // practically, this should only be called by addAngle
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000667 void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000668 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000669 SkASSERT(start != end);
670 fSegment = segment;
671 fStart = start;
672 fEnd = end;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000673 fDx = approximately_pin(pts[1].fX - pts[0].fX); // b - a
674 fDy = approximately_pin(pts[1].fY - pts[0].fY);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000675 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000676 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000677 return;
678 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000679 fDDx = approximately_pin(pts[2].fX - pts[1].fX - fDx); // a - 2b + c
680 fDDy = approximately_pin(pts[2].fY - pts[1].fY - fDy);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000681 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000682 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000683 return;
684 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000685 fDDDx = approximately_pin(pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX);
686 fDDDy = approximately_pin(pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000687 }
688
689 // noncoincident quads/cubics may have the same initial angle
690 // as lines, so must sort by derivatives as well
691 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000692 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000693 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000694 fSegment = segment;
695 fStart = start;
696 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000697 fDx = pts[1].fX - pts[0].fX; // b - a
698 fDy = pts[1].fY - pts[0].fY;
699 if (verb == SkPath::kLine_Verb) {
700 fDDx = fDDy = fDDDx = fDDDy = 0;
701 return;
702 }
703 if (verb == SkPath::kQuad_Verb) {
704 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
705 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
706 int larger = std::max(abs(uplsX), abs(uplsY));
707 int shift = 0;
708 double flatT;
709 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
710 LineParameters implicitLine;
caryclark@google.com32546db2012-08-31 20:55:07 +0000711 MAKE_CONST_LINE(tangent, pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000712 implicitLine.lineEndPoints(tangent);
713 implicitLine.normalize();
714 while (larger > UlpsEpsilon * 1024) {
715 larger >>= 2;
716 ++shift;
717 flatT = 0.5 / (1 << shift);
718 QuadXYAtT(pts, flatT, &ddPt);
719 _Point _pt = {ddPt.fX, ddPt.fY};
720 double distance = implicitLine.pointDistance(_pt);
721 if (approximately_zero(distance)) {
722 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
723 break;
724 }
725 }
726 flatT = 0.5 / (1 << shift);
727 QuadXYAtT(pts, flatT, &ddPt);
728 fDDx = ddPt.fX - pts[0].fX;
729 fDDy = ddPt.fY - pts[0].fY;
730 SkASSERT(fDDx != 0 || fDDy != 0);
731 fDDDx = fDDDy = 0;
732 return;
733 }
734 SkASSERT(0); // FIXME: add cubic case
735 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000736#endif
rmistry@google.comd6176b02012-08-23 18:14:13 +0000737
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000738 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000739 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000740 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000741
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000742 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000743 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000744 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000745
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000746 int start() const {
747 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000748 }
749
caryclark@google.comc899ad92012-08-23 15:24:42 +0000750#if DEBUG_ANGLE
751 void debugShow(const SkPoint& a) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000752#if HIGH_DEF_ANGLES
753 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n",
754 dx(), dy(), fSide);
755#else
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000756 SkDebugf(" d=(%1.9g,%1.9g) dd=(%1.9g,%1.9g) ddd=(%1.9g,%1.9g)\n",
caryclark@google.comc899ad92012-08-23 15:24:42 +0000757 fDx, fDy, fDDx, fDDy, fDDDx, fDDDy);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000758 AngleValue ax = (AngleValue) a.fX;
759 AngleValue ay = (AngleValue) a.fY;
760 AngleValue bx, by, cx, cy, dx, dy;
761 bx = ax + fDx; // add b - a
762 by = ay + fDy;
763 cx = ax + 2 * fDx + fDDx; // add a + 2(b - a) to a - 2b + c
764 cy = ay + 2 * fDy + fDDy;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000765 if (fDDDx == 0 && fDDDy == 0) {
766 if (fDDx == 0 && fDDy == 0) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000767 SkDebugf(
768" {SkPath::kLine_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g} }},\n",
769 ax, ay, bx, by);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000770 } else {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000771 SkDebugf(
772" {SkPath::kQuad_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}}},\n",
773 ax, ay, bx, by, cx, cy);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000774 }
775 } else {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000776 dx = fDDDx - ax - 3 * (cx - bx);
777 dy = fDDDy - ay - 3 * (cy - by);
778 SkDebugf(
779" {SkPath::kCubic_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}}},\n",
780 ax, ay, bx, by, cx, cy, dx, dy);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000781 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000782#endif
caryclark@google.comc899ad92012-08-23 15:24:42 +0000783 }
784#endif
785
caryclark@google.com15fa1382012-05-07 20:49:36 +0000786private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000787#if HIGH_DEF_ANGLES
788 const SkPoint* fPts;
789 Quadratic fQ;
790 SkPath::Verb fVerb;
791 double fSide;
792 LineParameters fTangent1;
793#else
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000794 AngleValue fDx;
795 AngleValue fDy;
796 AngleValue fDDx;
797 AngleValue fDDy;
798 AngleValue fDDDx;
799 AngleValue fDDDy;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000800#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000801 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000802 int fStart;
803 int fEnd;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000804};
805
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000806static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
807 int angleCount = angles.count();
808 int angleIndex;
809 angleList.setReserve(angleCount);
810 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
811 *angleList.append() = &angles[angleIndex];
812 }
813 QSort<Angle>(angleList.begin(), angleList.end() - 1);
814}
815
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000816// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000817struct Bounds : public SkRect {
818 static bool Intersects(const Bounds& a, const Bounds& b) {
819 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
820 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
821 }
822
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000823 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
824 if (left < fLeft) {
825 fLeft = left;
826 }
827 if (top < fTop) {
828 fTop = top;
829 }
830 if (right > fRight) {
831 fRight = right;
832 }
833 if (bottom > fBottom) {
834 fBottom = bottom;
835 }
836 }
837
838 void add(const Bounds& toAdd) {
839 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
840 }
841
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000842 bool isEmpty() {
843 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000844 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000845 || isnan(fLeft) || isnan(fRight)
846 || isnan(fTop) || isnan(fBottom);
847 }
848
849 void setCubicBounds(const SkPoint a[4]) {
850 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000851 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000852 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000853 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
854 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000855 }
856
857 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000858 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000859 _Rect dRect;
860 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000861 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
862 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000863 }
864};
865
caryclark@google.com2ddff932012-08-07 21:25:27 +0000866static bool useInnerWinding(int outerWinding, int innerWinding) {
867 SkASSERT(outerWinding != innerWinding);
868 int absOut = abs(outerWinding);
869 int absIn = abs(innerWinding);
870 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
871 if (outerWinding * innerWinding < 0) {
872#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000873 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000874 outerWinding, innerWinding, result ? "true" : "false");
875#endif
876 }
877 return result;
878}
879
caryclark@google.com15fa1382012-05-07 20:49:36 +0000880struct Span {
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000881 Segment* fOther;
caryclark@google.com27c449a2012-07-27 18:26:38 +0000882 mutable SkPoint fPt; // lazily computed as needed
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000883 double fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000884 double fOtherT; // value at fOther[fOtherIndex].fT
885 int fOtherIndex; // can't be used during intersection
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000886 int fWindSum; // accumulated from contours surrounding this one
887 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com235f56a2012-09-14 14:19:30 +0000888 int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000889 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000890};
891
caryclark@google.com235f56a2012-09-14 14:19:30 +0000892static const bool opLookup[][2][2] = {
893 // ==0 !=0
894 // b a b a
895 {{true , false}, {false, true }}, // a - b
896 {{false, false}, {true , true }}, // a & b
897 {{true , true }, {false, false}}, // a | b
898 {{true , true }, {true , true }}, // a ^ b
899};
900
901static bool activeOp(bool angleIsOp, int otherNonZero, ShapeOp op) {
902 return opLookup[op][otherNonZero][angleIsOp];
903}
904
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000905class Segment {
906public:
907 Segment() {
908#if DEBUG_DUMP
909 fID = ++gSegmentID;
910#endif
911 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000912
caryclark@google.com9764cc62012-07-12 19:29:45 +0000913 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
914 if (activeAngleInner(index, done, angles)) {
915 return true;
916 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000917 double referenceT = fTs[index].fT;
918 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000919 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000920 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000921 return true;
922 }
923 }
924 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000925 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000926 return true;
927 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000928 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000929 return false;
930 }
931
caryclark@google.com9764cc62012-07-12 19:29:45 +0000932 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000933 Span* span = &fTs[index];
934 Segment* other = span->fOther;
935 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +0000936 return other->activeAngleInner(oIndex, done, angles);
937 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000938
caryclark@google.com9764cc62012-07-12 19:29:45 +0000939 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
940 int next = nextSpan(index, 1);
941 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000942 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000943 if (upSpan.fWindValue) {
944 addAngle(angles, index, next);
945 if (upSpan.fDone) {
946 done++;
947 } else if (upSpan.fWindSum != SK_MinS32) {
948 return true;
949 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000950 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000951 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000952 int prev = nextSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000953 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +0000954 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000955 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000956 if (downSpan.fWindValue) {
957 addAngle(angles, index, prev);
958 if (downSpan.fDone) {
959 done++;
960 } else if (downSpan.fWindSum != SK_MinS32) {
961 return true;
962 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000963 }
964 }
965 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000966 }
967
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000968 SkScalar activeTop() const {
969 SkASSERT(!done());
970 int count = fTs.count();
971 SkScalar result = SK_ScalarMax;
972 bool lastDone = true;
973 for (int index = 0; index < count; ++index) {
974 bool done = fTs[index].fDone;
975 if (!done || !lastDone) {
976 SkScalar y = yAtT(index);
977 if (result > y) {
978 result = y;
979 }
980 }
981 lastDone = done;
982 }
983 SkASSERT(result < SK_ScalarMax);
984 return result;
985 }
986
987 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000988 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000989 Angle* angle = angles.append();
990#if HIGH_DEF_ANGLES==0 // old way
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000991 SkPoint edge[4];
992 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000993 angle->set(edge, fVerb, this, start, end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000994#else // new way : compute temp edge in higher precision
995 angle->set(fPts, fVerb, this, start, end, fTs[start].fT, fTs[end].fT);
996#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000997 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000998
caryclark@google.com2ddff932012-08-07 21:25:27 +0000999 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001000 double oEnd) {
1001 int tIndex = -1;
1002 int tCount = fTs.count();
1003 int oIndex = -1;
1004 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001005 do {
1006 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001007 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001008 int tIndexStart = tIndex;
1009 do {
1010 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001011 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001012 int oIndexStart = oIndex;
1013 double nextT;
1014 do {
1015 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001016 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001017 double oNextT;
1018 do {
1019 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001020 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001021 // at this point, spans before and after are at:
1022 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1023 // if tIndexStart == 0, no prior span
1024 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001025
caryclark@google.comcc905052012-07-25 20:59:42 +00001026 // advance the span with zero winding
1027 // if the following span exists (not past the end, non-zero winding)
1028 // connect the two edges
1029 if (!fTs[tIndexStart].fWindValue) {
1030 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1031 #if DEBUG_CONCIDENT
1032 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1033 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001034 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1035 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001036 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001037 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001038 }
1039 if (nextT < 1 && fTs[tIndex].fWindValue) {
1040 #if DEBUG_CONCIDENT
1041 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1042 __FUNCTION__, fID, other.fID, tIndex,
1043 fTs[tIndex].fT, xyAtT(tIndex).fX,
1044 xyAtT(tIndex).fY);
1045 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001046 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001047 }
1048 } else {
1049 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1050 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1051 #if DEBUG_CONCIDENT
1052 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1053 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001054 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1055 other.xyAtT(oIndexStart).fY);
1056 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001057 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001058 }
1059 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1060 #if DEBUG_CONCIDENT
1061 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1062 __FUNCTION__, fID, other.fID, oIndex,
1063 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1064 other.xyAtT(oIndex).fY);
1065 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1066 #endif
1067 }
1068 }
1069 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001070
caryclark@google.comcc905052012-07-25 20:59:42 +00001071 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1072 double oEnd) {
1073 // walk this to outsideTs[0]
1074 // walk other to outsideTs[1]
1075 // if either is > 0, add a pointer to the other, copying adjacent winding
1076 int tIndex = -1;
1077 int oIndex = -1;
1078 double tStart = outsideTs[0];
1079 double oStart = outsideTs[1];
1080 do {
1081 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001082 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001083 do {
1084 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001085 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001086 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001087 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001088 }
1089 tStart = fTs[tIndex].fT;
1090 oStart = other.fTs[oIndex].fT;
1091 do {
1092 double nextT;
1093 do {
1094 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001095 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001096 tStart = nextT;
1097 do {
1098 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001099 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001100 oStart = nextT;
1101 if (tStart == 1 && oStart == 1) {
1102 break;
1103 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001104 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001105 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001106 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001107
caryclark@google.com235f56a2012-09-14 14:19:30 +00001108 void addCubic(const SkPoint pts[4], bool operand) {
1109 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001110 fBounds.setCubicBounds(pts);
1111 }
1112
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001113 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001114 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001115 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001116 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001117 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001118 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001119 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001120 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
1121 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
1122 if (fVerb > 1) {
1123 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
1124 }
1125 if (fVerb > 2) {
1126 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
1127 }
1128 SkDebugf("\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001129 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001130 switch (fVerb) {
1131 case SkPath::kLine_Verb:
1132 path.lineTo(edge[1].fX, edge[1].fY);
1133 break;
1134 case SkPath::kQuad_Verb:
1135 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
1136 break;
1137 case SkPath::kCubic_Verb:
1138 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
1139 edge[3].fX, edge[3].fY);
1140 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001141 default:
1142 SkASSERT(0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001143 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001144 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001145 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001146 }
1147
caryclark@google.com235f56a2012-09-14 14:19:30 +00001148 void addLine(const SkPoint pts[2], bool operand) {
1149 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001150 fBounds.set(pts, 2);
1151 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001152
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001153 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
1154 const SkPoint& pt = xyAtT(tIndex);
1155 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001156 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001157 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001158 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001159 path.moveTo(pt.fX, pt.fY);
1160 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001161 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001162 }
1163
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001164 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001165 void addOtherT(int index, double otherT, int otherIndex) {
1166 Span& span = fTs[index];
1167 span.fOtherT = otherT;
1168 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001169 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001170
caryclark@google.com235f56a2012-09-14 14:19:30 +00001171 void addQuad(const SkPoint pts[3], bool operand) {
1172 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001173 fBounds.setQuadBounds(pts);
1174 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001175
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001176 // Defer all coincident edge processing until
1177 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001178
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001179// no need to be tricky; insert in normal T order
1180// resolve overlapping ts when considering coincidence later
1181
1182 // add non-coincident intersection. Resulting edges are sorted in T.
1183 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001184 // FIXME: in the pathological case where there is a ton of intercepts,
1185 // binary search?
1186 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001187 size_t tCount = fTs.count();
caryclark@google.comc899ad92012-08-23 15:24:42 +00001188 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001189 if (approximately_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001190 newT = 0;
1191 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001192 if (approximately_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001193 newT = 1;
1194 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001195 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001196 // OPTIMIZATION: if there are three or more identical Ts, then
1197 // the fourth and following could be further insertion-sorted so
1198 // that all the edges are clockwise or counterclockwise.
1199 // This could later limit segment tests to the two adjacent
1200 // neighbors, although it doesn't help with determining which
1201 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001202 if (newT < fTs[index].fT) {
1203 insertedAt = index;
1204 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001205 }
1206 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001207 Span* span;
1208 if (insertedAt >= 0) {
1209 span = fTs.insert(insertedAt);
1210 } else {
1211 insertedAt = tCount;
1212 span = fTs.append();
1213 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001214 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001215 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001216 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001217 span->fWindSum = SK_MinS32;
1218 span->fWindValue = 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001219 span->fWindValueOpp = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001220 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001221 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001222 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001223 return insertedAt;
1224 }
1225
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001226 // set spans from start to end to decrement by one
1227 // note this walks other backwards
1228 // FIMXE: there's probably an edge case that can be constructed where
1229 // two span in one segment are separated by float epsilon on one span but
1230 // not the other, if one segment is very small. For this
1231 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001232 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001233 // sorted? It feels better in such a case to match the span's other span
1234 // pointer since both coincident segments must contain the same spans.
1235 void addTCancel(double startT, double endT, Segment& other,
1236 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001237 SkASSERT(!approximately_negative(endT - startT));
1238 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001239 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001240 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001241 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001242 ++index;
1243 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001244 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001245 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001246 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001247 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001248 Span* test = &fTs[index];
1249 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001250 SkTDArray<double> outsideTs;
1251 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001252 do {
1253 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001254 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001255 double testT = test->fT;
1256 double oTestT = oTest->fT;
1257 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001258 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001259 if (decrement) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001260 if (binary) {
1261 --(span->fWindValueOpp);
1262 } else {
1263 decrementSpan(span);
1264 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001265 } else if (track && span->fT < 1 && oTestT < 1) {
1266 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001267 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001268 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001269 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001270 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001271 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1272 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1273 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001274 while (approximately_negative(otherTMatchStart - oSpan->fT)
1275 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001276 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001277 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001278 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001279 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001280 other.decrementSpan(oSpan);
1281 } else if (track && oSpan->fT < 1 && testT < 1) {
1282 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001283 }
1284 if (!oIndex) {
1285 break;
1286 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001287 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001288 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001289 test = span;
1290 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001291 } while (!approximately_negative(endT - test->fT));
1292 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001293 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001294 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001295 double tStart = outsideTs[0];
1296 double oStart = outsideTs[1];
1297 addCancelOutsides(tStart, oStart, other, oEndT);
1298 int count = outsideTs.count();
1299 if (count > 2) {
1300 double tStart = outsideTs[count - 2];
1301 double oStart = outsideTs[count - 1];
1302 addCancelOutsides(tStart, oStart, other, oEndT);
1303 }
caryclark@google.com18063442012-07-25 12:05:18 +00001304 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001305 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001306 double tStart = oOutsideTs[0];
1307 double oStart = oOutsideTs[1];
1308 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001309 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001310 }
1311
1312 // set spans from start to end to increment the greater by one and decrement
1313 // the lesser
caryclark@google.com235f56a2012-09-14 14:19:30 +00001314 void addTCoincident(const bool isXor, double startT, double endT,
1315 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001316 SkASSERT(!approximately_negative(endT - startT));
1317 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001318 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001319 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001320 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001321 ++index;
1322 }
1323 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001324 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001325 ++oIndex;
1326 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001327 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001328 Span* test = &fTs[index];
1329 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001330 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001331 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001332 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001333 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001334 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001335 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001336 bool decrementThis = (test->fWindValue < oTest->fWindValue) & !isXor;
1337 bool decrementOther = (test->fWindValue >= oTest->fWindValue) & !isXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001338 Span* end = test;
1339 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001340 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001341 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001342 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001343 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001344 if (transfer) {
1345 if (decrementOther) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001346 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001347 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001348 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001349 if (binary) {
1350 ++(end->fWindValueOpp);
1351 } else {
1352 ++(end->fWindValue);
1353 }
caryclark@google.com18063442012-07-25 12:05:18 +00001354 } else if (decrementSpan(end)) {
1355 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001356 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001357 } else if (oTest->fWindValue) {
1358 SkASSERT(!decrementOther);
1359 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1360 TrackOutside(xOutsideTs, end->fT, oStartT);
1361 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001362 }
1363 end = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001364 } while (approximately_negative(end->fT - test->fT));
caryclark@google.com59823f72012-08-09 18:17:47 +00001365 // because of the order in which coincidences are resolved, this and other
1366 // may not have the same intermediate points. Compute the corresponding
1367 // intermediate T values (using this as the master, other as the follower)
1368 // and walk other conditionally -- hoping that it catches up in the end
1369 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001370 Span* oEnd = oTest;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001371 while (!approximately_negative(oEndT - oEnd->fT) && approximately_negative(oEnd->fT - otherTMatch)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00001372 if (transfer) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001373 if (decrementThis) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001374 #ifdef SK_DEBUG
1375 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1376 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001377 if (binary) {
1378 ++(oEnd->fWindValueOpp);
1379 } else {
1380 ++(oEnd->fWindValue);
1381 }
caryclark@google.com18063442012-07-25 12:05:18 +00001382 } else if (other.decrementSpan(oEnd)) {
1383 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001384 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001385 } else if (test->fWindValue) {
1386 SkASSERT(!decrementOther);
1387 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1388 SkASSERT(0); // track for later?
1389 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001390 }
1391 oEnd = &other.fTs[++oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001392 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001393 test = end;
1394 oTest = oEnd;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001395 } while (!approximately_negative(endT - test->fT));
1396 SkASSERT(approximately_negative(oTest->fT - oEndT));
1397 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001398 if (!done()) {
1399 if (outsideTs.count()) {
1400 addCoinOutsides(outsideTs, other, oEndT);
1401 }
1402 if (xOutsideTs.count()) {
1403 addCoinOutsides(xOutsideTs, other, oEndT);
1404 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001405 }
1406 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001407 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001408 }
1409 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001410
caryclark@google.comcc905052012-07-25 20:59:42 +00001411 // FIXME: this doesn't prevent the same span from being added twice
1412 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001413 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001414 int tCount = fTs.count();
1415 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1416 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001417 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001418 break;
1419 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001420 if (approximately_negative(span.fT - t) && span.fOther == &other && span.fOtherT == otherT) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001421#if DEBUG_ADD_T_PAIR
1422 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1423 __FUNCTION__, fID, t, other.fID, otherT);
1424#endif
1425 return;
1426 }
1427 }
caryclark@google.com47580692012-07-23 12:14:49 +00001428#if DEBUG_ADD_T_PAIR
1429 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1430 __FUNCTION__, fID, t, other.fID, otherT);
1431#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001432 int insertedAt = addT(t, &other);
1433 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001434 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001435 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001436 matchWindingValue(insertedAt, t, borrowWind);
1437 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001438 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001439
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001440 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001441 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001442 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1443 addAngle(angles, end, start);
1444 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001445 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001446 int step = SkSign32(end - start);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001447 int tIndex = nextSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001448 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001449 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001450 }
1451 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001452
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001453 const Bounds& bounds() const {
1454 return fBounds;
1455 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001456
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001457 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001458 double referenceT = fTs[index].fT;
1459 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001460 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001461 buildAnglesInner(lesser, angles);
1462 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001463 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001464 buildAnglesInner(index, angles);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001465 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001466 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001467
1468 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1469 Span* span = &fTs[index];
1470 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001471 // if there is only one live crossing, and no coincidence, continue
1472 // in the same direction
1473 // if there is coincidence, the only choice may be to reverse direction
1474 // find edge on either side of intersection
1475 int oIndex = span->fOtherIndex;
1476 // if done == -1, prior span has already been processed
1477 int step = 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001478 int next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001479 if (next < 0) {
1480 step = -step;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001481 next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001482 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001483 // add candidate into and away from junction
1484 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001485 }
1486
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001487 // figure out if the segment's ascending T goes clockwise or not
1488 // not enough context to write this as shown
1489 // instead, add all segments meeting at the top
1490 // sort them using buildAngleList
1491 // find the first in the sort
1492 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001493 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001494 SkASSERT(0); // incomplete
1495 return false;
1496 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001497
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001498 int computeSum(int startIndex, int endIndex) {
1499 SkTDArray<Angle> angles;
1500 addTwoAngles(startIndex, endIndex, angles);
1501 buildAngles(endIndex, angles);
1502 SkTDArray<Angle*> sorted;
1503 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001504#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00001505 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001506#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001507 int angleCount = angles.count();
1508 const Angle* angle;
1509 const Segment* base;
1510 int winding;
1511 int firstIndex = 0;
1512 do {
1513 angle = sorted[firstIndex];
1514 base = angle->segment();
1515 winding = base->windSum(angle);
1516 if (winding != SK_MinS32) {
1517 break;
1518 }
1519 if (++firstIndex == angleCount) {
1520 return SK_MinS32;
1521 }
1522 } while (true);
1523 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001524 int spanWinding = base->spanSign(angle);
1525 bool inner = useInnerWinding(winding + spanWinding, winding);
1526 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001527 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001528 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001529 inner ? winding + spanWinding : winding);
1530 #endif
1531 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001532 winding += spanWinding;
1533 }
1534 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001535 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001536 #endif
1537 int nextIndex = firstIndex + 1;
1538 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001539 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001540 do {
1541 if (nextIndex == angleCount) {
1542 nextIndex = 0;
1543 }
1544 angle = sorted[nextIndex];
1545 Segment* segment = angle->segment();
1546 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001547 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001548 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001549 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001550 maxWinding = winding;
1551 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001552 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001553 }
1554 } while (++nextIndex != lastIndex);
1555 return windSum(SkMin32(startIndex, endIndex));
1556 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001557
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001558 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001559 int bestT = -1;
1560 SkScalar top = bounds().fTop;
1561 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001562 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001563 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001564 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001565 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001566 if (fTs[start].fWindValue == 0) {
1567 continue;
1568 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001569 SkPoint edge[4];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001570 // OPTIMIZE: wrap this so that if start==0 end==fTCount-1 we can
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001571 // work with the original data directly
caryclark@google.com24bec792012-08-20 12:43:57 +00001572 double startT = fTs[start].fT;
1573 double endT = fTs[end].fT;
1574 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001575 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001576 Intersections intersections;
1577 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1578 false, intersections);
1579 if (pts == 0) {
1580 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001581 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001582 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1583 // if the intersection is edge on, wait for another one
1584 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001585 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001586 for (int index = 0; index < pts; ++index) {
1587 SkPoint pt;
1588 double foundT = intersections.fT[0][index];
1589 double testT = startT + (endT - startT) * foundT;
1590 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1591 if (bestY < pt.fY && pt.fY < basePt.fY) {
1592 bestY = pt.fY;
1593 bestT = foundT < 1 ? start : end;
1594 hitT = testT;
1595 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001596 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001597 } while (fTs[end].fT != 1);
1598 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001599 }
caryclark@google.com18063442012-07-25 12:05:18 +00001600
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001601 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1602 // if a segment is connected to this one, consider it crossing
1603 int tIndex;
1604 if (fPts[0].fX == basePt.fX) {
1605 tIndex = 0;
1606 do {
1607 const Span& sSpan = fTs[tIndex];
1608 const Segment* sOther = sSpan.fOther;
1609 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1610 continue;
1611 }
1612 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1613 : sOther->fBounds.fRight > basePt.fX) {
1614 return true;
1615 }
1616 } while (fTs[++tIndex].fT == 0);
1617 }
1618 if (fPts[fVerb].fX == basePt.fX) {
1619 tIndex = fTs.count() - 1;
1620 do {
1621 const Span& eSpan = fTs[tIndex];
1622 const Segment* eOther = eSpan.fOther;
1623 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1624 continue;
1625 }
1626 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1627 : eOther->fBounds.fRight > basePt.fX) {
1628 return true;
1629 }
1630 } while (fTs[--tIndex].fT == 1);
1631 }
1632 return false;
1633 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001634
caryclark@google.com18063442012-07-25 12:05:18 +00001635 bool decrementSpan(Span* span) {
1636 SkASSERT(span->fWindValue > 0);
1637 if (--(span->fWindValue) == 0) {
1638 span->fDone = true;
1639 ++fDoneSpans;
1640 return true;
1641 }
1642 return false;
1643 }
1644
caryclark@google.com15fa1382012-05-07 20:49:36 +00001645 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001646 SkASSERT(fDoneSpans <= fTs.count());
1647 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001648 }
1649
caryclark@google.com47580692012-07-23 12:14:49 +00001650 bool done(const Angle& angle) const {
1651 int start = angle.start();
1652 int end = angle.end();
1653 const Span& mSpan = fTs[SkMin32(start, end)];
1654 return mSpan.fDone;
1655 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001656
1657 Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
1658 int& nextStart, int& nextEnd, int& winding, int& spanWinding, ShapeOp op,
1659 const int aXorMask, const int bXorMask) {
1660 const int startIndex = nextStart;
1661 const int endIndex = nextEnd;
1662 int outerWinding = winding;
1663 int innerWinding = winding + spanWinding;
1664 #if DEBUG_WINDING
1665 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1666 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
1667 #endif
1668 if (useInnerWinding(outerWinding, innerWinding)) {
1669 outerWinding = innerWinding;
1670 }
1671 SkASSERT(startIndex != endIndex);
1672 int count = fTs.count();
1673 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1674 : startIndex > 0);
1675 int step = SkSign32(endIndex - startIndex);
1676 int end = nextSpan(startIndex, step);
1677 SkASSERT(end >= 0);
1678 Span* endSpan = &fTs[end];
1679 Segment* other;
1680 if (isSimple(end)) {
1681 // mark the smaller of startIndex, endIndex done, and all adjacent
1682 // spans with the same T value (but not 'other' spans)
1683 #if DEBUG_WINDING
1684 SkDebugf("%s simple\n", __FUNCTION__);
1685 #endif
1686 markDone(SkMin32(startIndex, endIndex), outerWinding);
1687 other = endSpan->fOther;
1688 nextStart = endSpan->fOtherIndex;
1689 double startT = other->fTs[nextStart].fT;
1690 nextEnd = nextStart;
1691 do {
1692 nextEnd += step;
1693 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
1694 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1695 return other;
1696 }
1697 // more than one viable candidate -- measure angles to find best
1698 SkTDArray<Angle> angles;
1699 SkASSERT(startIndex - endIndex != 0);
1700 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1701 addTwoAngles(startIndex, end, angles);
1702 buildAngles(end, angles);
1703 SkTDArray<Angle*> sorted;
1704 sortAngles(angles, sorted);
1705 int angleCount = angles.count();
1706 int firstIndex = findStartingEdge(sorted, startIndex, end);
1707 SkASSERT(firstIndex >= 0);
1708 #if DEBUG_SORT
1709 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
1710 #endif
1711 SkASSERT(sorted[firstIndex]->segment() == this);
1712 #if DEBUG_WINDING
1713 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
1714 #endif
1715 int aSumWinding = winding;
1716 int bSumWinding = winding;
1717 bool angleIsOp = sorted[firstIndex]->segment()->operand();
1718 int angleSpan = spanSign(sorted[firstIndex]);
1719 if (angleIsOp) {
1720 bSumWinding -= angleSpan;
1721 } else {
1722 aSumWinding -= angleSpan;
1723 }
1724 int nextIndex = firstIndex + 1;
1725 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1726 const Angle* foundAngle = NULL;
1727 // FIXME: found done logic probably fails if there are more than 4
1728 // sorted angles. It should bias towards the first and last undone
1729 // edges -- but not sure that it won't choose a middle (incorrect)
1730 // edge if one is undone
1731 bool foundDone = false;
1732 bool foundDone2 = false;
1733 // iterate through the angle, and compute everyone's winding
1734 bool altFlipped = false;
1735 bool foundFlipped = false;
1736 int foundMax = SK_MinS32;
1737 int foundSum = SK_MinS32;
1738 Segment* nextSegment;
1739 int lastNonZeroSum = winding;
1740 do {
1741 if (nextIndex == angleCount) {
1742 nextIndex = 0;
1743 }
1744 const Angle* nextAngle = sorted[nextIndex];
1745 nextSegment = nextAngle->segment();
1746 angleIsOp = nextSegment->operand();
1747 int sumWinding = angleIsOp ? bSumWinding : aSumWinding;
1748 int maxWinding = sumWinding;
1749 if (sumWinding) {
1750 lastNonZeroSum = sumWinding;
1751 }
1752 sumWinding -= nextSegment->spanSign(nextAngle);
1753 int xorMask = nextSegment->operand() ? bXorMask : aXorMask;
1754 bool otherNonZero;
1755 if (angleIsOp) {
1756 bSumWinding = sumWinding;
1757 otherNonZero = aSumWinding & aXorMask;
1758 } else {
1759 aSumWinding = sumWinding;
1760 otherNonZero = bSumWinding & bXorMask;
1761 }
1762 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
1763 #if DEBUG_WINDING
1764 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
1765 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
1766 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
1767 #endif
1768
1769 if (!(sumWinding & xorMask) && activeOp(angleIsOp, otherNonZero, op)) {
1770 if (!active) {
1771 markDone(SkMin32(startIndex, endIndex), outerWinding);
1772 // FIXME: seems like a bug that this isn't calling userInnerWinding
1773 nextSegment->markWinding(SkMin32(nextAngle->start(),
1774 nextAngle->end()), maxWinding);
1775 #if DEBUG_WINDING
1776 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
1777 #endif
1778 return NULL;
1779 }
1780 if (!foundAngle || foundDone) {
1781 foundAngle = nextAngle;
1782 foundDone = nextSegment->done(*nextAngle);
1783 foundFlipped = altFlipped;
1784 foundMax = maxWinding;
1785 }
1786 continue;
1787 }
1788 if (!(maxWinding & xorMask) && (!foundAngle || foundDone2)
1789 && activeOp(angleIsOp, otherNonZero, op)) {
1790 #if DEBUG_WINDING
1791 if (foundAngle && foundDone2) {
1792 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
1793 }
1794 #endif
1795 foundAngle = nextAngle;
1796 foundDone2 = nextSegment->done(*nextAngle);
1797 foundFlipped = altFlipped;
1798 foundSum = sumWinding;
1799 }
1800 if (nextSegment->done()) {
1801 continue;
1802 }
1803 // if the winding is non-zero, nextAngle does not connect to
1804 // current chain. If we haven't done so already, mark the angle
1805 // as done, record the winding value, and mark connected unambiguous
1806 // segments as well.
1807 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
1808 if (useInnerWinding(maxWinding, sumWinding)) {
1809 maxWinding = sumWinding;
1810 }
1811 Span* last;
1812 if (foundAngle) {
1813 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
1814 } else {
1815 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
1816 }
1817 if (last) {
1818 *chase.append() = last;
1819 }
1820 }
1821 } while (++nextIndex != lastIndex);
1822 markDone(SkMin32(startIndex, endIndex), outerWinding);
1823 if (!foundAngle) {
1824 return NULL;
1825 }
1826 nextStart = foundAngle->start();
1827 nextEnd = foundAngle->end();
1828 nextSegment = foundAngle->segment();
1829 int flipped = foundFlipped ? -1 : 1;
1830 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
1831 SkMin32(nextStart, nextEnd));
1832 if (winding) {
1833 #if DEBUG_WINDING
1834 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
1835 if (foundSum == SK_MinS32) {
1836 SkDebugf("?");
1837 } else {
1838 SkDebugf("%d", foundSum);
1839 }
1840 SkDebugf(" foundMax=");
1841 if (foundMax == SK_MinS32) {
1842 SkDebugf("?");
1843 } else {
1844 SkDebugf("%d", foundMax);
1845 }
1846 SkDebugf("\n");
1847 #endif
1848 winding = foundSum;
1849 }
1850 #if DEBUG_WINDING
1851 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
1852 #endif
1853 return nextSegment;
1854 }
caryclark@google.com47580692012-07-23 12:14:49 +00001855
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001856 // so the span needs to contain the pairing info found here
1857 // this should include the winding computed for the edge, and
1858 // what edge it connects to, and whether it is discarded
1859 // (maybe discarded == abs(winding) > 1) ?
1860 // only need derivatives for duration of sorting, add a new struct
1861 // for pairings, remove extra spans that have zero length and
1862 // reference an unused other
1863 // for coincident, the last span on the other may be marked done
1864 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001865
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001866 // if loop is exhausted, contour may be closed.
1867 // FIXME: pass in close point so we can check for closure
1868
1869 // given a segment, and a sense of where 'inside' is, return the next
1870 // segment. If this segment has an intersection, or ends in multiple
1871 // segments, find the mate that continues the outside.
1872 // note that if there are multiples, but no coincidence, we can limit
1873 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00001874
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001875 // mark found segments as done
1876
caryclark@google.com15fa1382012-05-07 20:49:36 +00001877 // start is the index of the beginning T of this edge
1878 // it is guaranteed to have an end which describes a non-zero length (?)
1879 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00001880 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
1881 int& nextStart, int& nextEnd, int& winding, int& spanWinding) {
1882 const int startIndex = nextStart;
1883 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001884 int outerWinding = winding;
1885 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00001886 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001887 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1888 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00001889 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001890 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001891 outerWinding = innerWinding;
1892 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001893 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001894 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001895 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1896 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001897 int step = SkSign32(endIndex - startIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001898 int end = nextSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001899 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001900 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001901 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001902 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001903 // mark the smaller of startIndex, endIndex done, and all adjacent
1904 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001905 #if DEBUG_WINDING
1906 SkDebugf("%s simple\n", __FUNCTION__);
1907 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001908 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001909 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001910 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00001911 double startT = other->fTs[nextStart].fT;
1912 nextEnd = nextStart;
1913 do {
1914 nextEnd += step;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001915 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00001916 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001917 return other;
1918 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001919 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001920 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001921 SkASSERT(startIndex - endIndex != 0);
1922 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001923 addTwoAngles(startIndex, end, angles);
1924 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001925 SkTDArray<Angle*> sorted;
1926 sortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001927 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001928 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001929 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00001930 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001931 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001932 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001933 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001934 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001935 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00001936 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001937 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001938 int nextIndex = firstIndex + 1;
1939 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1940 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00001941 // FIXME: found done logic probably fails if there are more than 4
1942 // sorted angles. It should bias towards the first and last undone
1943 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001944 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00001945 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00001946 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001947 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00001948 bool altFlipped = false;
1949 bool foundFlipped = false;
1950 int foundMax = SK_MinS32;
1951 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001952 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00001953 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001954 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001955 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001956 nextIndex = 0;
1957 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001958 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00001959 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00001960 if (sumWinding) {
1961 lastNonZeroSum = sumWinding;
1962 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00001963 nextSegment = nextAngle->segment();
caryclark@google.com2ddff932012-08-07 21:25:27 +00001964 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00001965 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001966 #if DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00001967 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00001968 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
1969 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001970 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00001971 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00001972 if (!active) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001973 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001974 // FIXME: seems like a bug that this isn't calling userInnerWinding
caryclark@google.com47580692012-07-23 12:14:49 +00001975 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00001976 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001977 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001978 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001979 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00001980 return NULL;
1981 }
caryclark@google.com47580692012-07-23 12:14:49 +00001982 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001983 foundAngle = nextAngle;
caryclark@google.com47580692012-07-23 12:14:49 +00001984 foundDone = nextSegment->done(*nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00001985 foundFlipped = altFlipped;
1986 foundMax = maxWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001987 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001988 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001989 }
caryclark@google.com24bec792012-08-20 12:43:57 +00001990 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00001991 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001992 if (foundAngle && foundDone2) {
1993 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001994 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00001995 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00001996 foundAngle = nextAngle;
caryclark@google.com24bec792012-08-20 12:43:57 +00001997 foundDone2 = nextSegment->done(*nextAngle);
1998 foundFlipped = altFlipped;
1999 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002000 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002001 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002002 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002003 }
2004 // if the winding is non-zero, nextAngle does not connect to
2005 // current chain. If we haven't done so already, mark the angle
2006 // as done, record the winding value, and mark connected unambiguous
2007 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002008 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002009 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002010 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002011 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002012 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002013 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002014 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002015 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002016 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002017 }
2018 if (last) {
2019 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002020 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002021 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002022 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002023 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002024 if (!foundAngle) {
2025 return NULL;
2026 }
2027 nextStart = foundAngle->start();
2028 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002029 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002030 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002031 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2032 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002033 if (winding) {
2034 #if DEBUG_WINDING
2035 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2036 if (foundSum == SK_MinS32) {
2037 SkDebugf("?");
2038 } else {
2039 SkDebugf("%d", foundSum);
2040 }
2041 SkDebugf(" foundMax=");
2042 if (foundMax == SK_MinS32) {
2043 SkDebugf("?");
2044 } else {
2045 SkDebugf("%d", foundMax);
2046 }
2047 SkDebugf("\n");
2048 #endif
2049 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002050 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002051 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002052 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002053 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002054 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002055 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002056
caryclark@google.com24bec792012-08-20 12:43:57 +00002057 Segment* findNextXor(int& nextStart, int& nextEnd) {
2058 const int startIndex = nextStart;
2059 const int endIndex = nextEnd;
2060 SkASSERT(startIndex != endIndex);
2061 int count = fTs.count();
2062 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2063 : startIndex > 0);
2064 int step = SkSign32(endIndex - startIndex);
2065 int end = nextSpan(startIndex, step);
2066 SkASSERT(end >= 0);
2067 Span* endSpan = &fTs[end];
2068 Segment* other;
2069 markDone(SkMin32(startIndex, endIndex), 1);
2070 if (isSimple(end)) {
2071 #if DEBUG_WINDING
2072 SkDebugf("%s simple\n", __FUNCTION__);
2073 #endif
2074 other = endSpan->fOther;
2075 nextStart = endSpan->fOtherIndex;
2076 double startT = other->fTs[nextStart].fT;
2077 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002078 if ((approximately_less_than_zero(startT) && step < 0)
2079 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002080 step = -step;
2081 SkDEBUGCODE(firstLoop = false;)
2082 }
2083 do {
2084 nextEnd = nextStart;
2085 do {
2086 nextEnd += step;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002087 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002088 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2089 break;
2090 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002091 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002092 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002093 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002094 SkDEBUGCODE(firstLoop = false;)
2095 step = -step;
2096 } while (true);
2097 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2098 return other;
2099 }
2100 SkTDArray<Angle> angles;
2101 SkASSERT(startIndex - endIndex != 0);
2102 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2103 addTwoAngles(startIndex, end, angles);
2104 buildAngles(end, angles);
2105 SkTDArray<Angle*> sorted;
2106 sortAngles(angles, sorted);
2107 int angleCount = angles.count();
2108 int firstIndex = findStartingEdge(sorted, startIndex, end);
2109 SkASSERT(firstIndex >= 0);
2110 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002111 debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002112 #endif
2113 SkASSERT(sorted[firstIndex]->segment() == this);
2114 int nextIndex = firstIndex + 1;
2115 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2116 const Angle* nextAngle;
2117 Segment* nextSegment;
2118 do {
2119 if (nextIndex == angleCount) {
2120 nextIndex = 0;
2121 }
2122 nextAngle = sorted[nextIndex];
2123 nextSegment = nextAngle->segment();
2124 if (!nextSegment->done(*nextAngle)) {
2125 break;
2126 }
2127 if (++nextIndex == lastIndex) {
2128 return NULL;
2129 }
2130 } while (true);
2131 nextStart = nextAngle->start();
2132 nextEnd = nextAngle->end();
2133 return nextSegment;
2134 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002135
2136 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2137 int angleCount = sorted.count();
2138 int firstIndex = -1;
2139 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2140 const Angle* angle = sorted[angleIndex];
2141 if (angle->segment() == this && angle->start() == end &&
2142 angle->end() == start) {
2143 firstIndex = angleIndex;
2144 break;
2145 }
2146 }
2147 return firstIndex;
2148 }
2149
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002150 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com235f56a2012-09-14 14:19:30 +00002151 void findTooCloseToCall(bool isXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002152 int count = fTs.count();
2153 if (count < 3) { // require t=0, x, 1 at minimum
2154 return;
2155 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002156 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002157 int moCount;
2158 Span* match;
2159 Segment* mOther;
2160 do {
2161 match = &fTs[matchIndex];
2162 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002163 // FIXME: allow quads, cubics to be near coincident?
2164 if (mOther->fVerb == SkPath::kLine_Verb) {
2165 moCount = mOther->fTs.count();
2166 if (moCount >= 3) {
2167 break;
2168 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002169 }
2170 if (++matchIndex >= count) {
2171 return;
2172 }
2173 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002174 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002175 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002176 // look for a pair of nearby T values that map to the same (x,y) value
2177 // if found, see if the pair of other segments share a common point. If
2178 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002179 for (int index = matchIndex + 1; index < count; ++index) {
2180 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002181 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002182 continue;
2183 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002184 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002185 if (tOther->fVerb != SkPath::kLine_Verb) {
2186 continue; // FIXME: allow quads, cubics to be near coincident?
2187 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002188 int toCount = tOther->fTs.count();
2189 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002190 continue;
2191 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002192 const SkPoint* testPt = &xyAtT(test);
2193 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002194 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002195 moCount = toCount;
2196 match = test;
2197 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002198 matchPt = testPt;
2199 continue;
2200 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002201 int moStart = -1;
2202 int moEnd = -1;
2203 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002204 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002205 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002206 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002207 continue;
2208 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002209 if (moSpan.fOther == this) {
2210 if (moSpan.fOtherT == match->fT) {
2211 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002212 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002213 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002214 continue;
2215 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002216 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002217 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2218 moStart = -1;
2219 break;
2220 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002221 SkASSERT(moEnd == -1);
2222 moEnd = moIndex;
2223 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002224 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002225 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002226 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002227 continue;
2228 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002229 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002230 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002231 continue;
2232 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002233 int toStart = -1;
2234 int toEnd = -1;
2235 double toStartT, toEndT;
2236 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2237 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002238 if (toSpan.fDone) {
2239 continue;
2240 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002241 if (toSpan.fOther == this) {
2242 if (toSpan.fOtherT == test->fT) {
2243 toStart = toIndex;
2244 toStartT = toSpan.fT;
2245 }
2246 continue;
2247 }
2248 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002249 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2250 moStart = -1;
2251 break;
2252 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002253 SkASSERT(toEnd == -1);
2254 toEnd = toIndex;
2255 toEndT = toSpan.fT;
2256 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002257 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002258 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2259 if (toStart <= 0 || toEnd <= 0) {
2260 continue;
2261 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002262 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002263 continue;
2264 }
2265 // test to see if the segment between there and here is linear
2266 if (!mOther->isLinear(moStart, moEnd)
2267 || !tOther->isLinear(toStart, toEnd)) {
2268 continue;
2269 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002270 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002271 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002272 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002273 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002274 // FIXME: this is bogus for multiple ops
2275 // the xorMask needs to be accumulated from the union of the two
2276 // edges -- which means that the segment must have its own copy of the mask
2277 mOther->addTCoincident(isXor, moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002278 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002279 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002280 }
2281
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002282 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2283 // and use more concise logic like the old edge walker code?
2284 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002285 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002286 // iterate through T intersections and return topmost
2287 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002288 SkASSERT(!done());
2289 int firstT;
2290 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002291 SkPoint topPt;
2292 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002293 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002294 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002295 bool lastDone = true;
2296 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002297 const Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002298 if (!span.fDone || !lastDone) {
2299 const SkPoint& intercept = xyAtT(&span);
2300 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2301 && topPt.fX > intercept.fX)) {
2302 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002303 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002304 } else if (topPt == intercept) {
2305 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002306 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002307 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002308 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002309 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002310 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002311 int step = 1;
2312 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002313 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002314 step = -1;
2315 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002316 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002317 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002318 // if the topmost T is not on end, or is three-way or more, find left
2319 // look for left-ness from tLeft to firstT (matching y of other)
2320 SkTDArray<Angle> angles;
2321 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002322 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002323 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002324 SkTDArray<Angle*> sorted;
2325 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002326 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00002327 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002328 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002329 // skip edges that have already been processed
2330 firstT = -1;
2331 Segment* leftSegment;
2332 do {
2333 const Angle* angle = sorted[++firstT];
2334 leftSegment = angle->segment();
2335 tIndex = angle->end();
2336 endIndex = angle->start();
2337 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002338 return leftSegment;
2339 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002340
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002341 // FIXME: not crazy about this
2342 // when the intersections are performed, the other index is into an
2343 // incomplete array. as the array grows, the indices become incorrect
2344 // while the following fixes the indices up again, it isn't smart about
2345 // skipping segments whose indices are already correct
2346 // assuming we leave the code that wrote the index in the first place
2347 void fixOtherTIndex() {
2348 int iCount = fTs.count();
2349 for (int i = 0; i < iCount; ++i) {
2350 Span& iSpan = fTs[i];
2351 double oT = iSpan.fOtherT;
2352 Segment* other = iSpan.fOther;
2353 int oCount = other->fTs.count();
2354 for (int o = 0; o < oCount; ++o) {
2355 Span& oSpan = other->fTs[o];
2356 if (oT == oSpan.fT && this == oSpan.fOther) {
2357 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002358 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002359 }
2360 }
2361 }
2362 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002363
caryclark@google.com495f8e42012-05-31 13:13:11 +00002364 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002365 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002366 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002367 SkASSERT(end >= 0);
2368 if (multipleSpans(end)) {
2369 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002370 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002371 const Span& endSpan = fTs[end];
2372 Segment* other = endSpan.fOther;
2373 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002374 int otherEnd = other->nextSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002375 Span* last = other->innerChaseDone(index, step, winding);
2376 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002377 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002378 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002379
caryclark@google.com59823f72012-08-09 18:17:47 +00002380 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002381 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002382 SkASSERT(end >= 0);
2383 if (multipleSpans(end)) {
2384 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002385 }
2386 const Span& endSpan = fTs[end];
2387 Segment* other = endSpan.fOther;
2388 index = endSpan.fOtherIndex;
2389 int otherEnd = other->nextSpan(index, step);
2390 int min = SkMin32(index, otherEnd);
2391 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002392 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002393 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002394 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002395 Span* last = other->innerChaseWinding(index, step, winding);
2396 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002397 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002398 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002399
caryclark@google.com235f56a2012-09-14 14:19:30 +00002400 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2401 fDoneSpans = 0;
2402 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002403 fPts = pts;
2404 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002405 }
2406
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002407 bool intersected() const {
2408 return fTs.count() > 0;
2409 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002410
2411 bool isConnected(int startIndex, int endIndex) const {
2412 return fTs[startIndex].fWindSum != SK_MinS32
2413 || fTs[endIndex].fWindSum != SK_MinS32;
2414 }
2415
caryclark@google.com235f56a2012-09-14 14:19:30 +00002416 bool isHorizontal() const {
2417 return fBounds.fTop == fBounds.fBottom;
2418 }
2419
caryclark@google.com15fa1382012-05-07 20:49:36 +00002420 bool isLinear(int start, int end) const {
2421 if (fVerb == SkPath::kLine_Verb) {
2422 return true;
2423 }
2424 if (fVerb == SkPath::kQuad_Verb) {
2425 SkPoint qPart[3];
2426 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2427 return QuadIsLinear(qPart);
2428 } else {
2429 SkASSERT(fVerb == SkPath::kCubic_Verb);
2430 SkPoint cPart[4];
2431 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2432 return CubicIsLinear(cPart);
2433 }
2434 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002435
2436 // OPTIMIZE: successive calls could start were the last leaves off
2437 // or calls could specialize to walk forwards or backwards
2438 bool isMissing(double startT) const {
2439 size_t tCount = fTs.count();
2440 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002441 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002442 return false;
2443 }
2444 }
2445 return true;
2446 }
2447
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002448 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002449 int count = fTs.count();
2450 if (count == 2) {
2451 return true;
2452 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002453 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002454 if (approximately_less_than_zero(t)) {
2455 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002456 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002457 if (approximately_greater_than_one(t)) {
2458 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002459 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002460 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002461 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002462
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002463 bool isVertical() const {
2464 return fBounds.fLeft == fBounds.fRight;
2465 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002466
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002467 SkScalar leftMost(int start, int end) const {
2468 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2469 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002470
caryclark@google.com495f8e42012-05-31 13:13:11 +00002471 // this span is excluded by the winding rule -- chase the ends
2472 // as long as they are unambiguous to mark connections as done
2473 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002474 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002475 int index = angle->start();
2476 int endIndex = angle->end();
2477 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002478 Span* last = innerChaseDone(index, step, winding);
2479 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002480 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002481 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002482
caryclark@google.com59823f72012-08-09 18:17:47 +00002483 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002484 int index = angle->start();
2485 int endIndex = angle->end();
2486 int min = SkMin32(index, endIndex);
2487 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002488 Span* last = innerChaseWinding(index, step, winding);
2489 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002490 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002491 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002492
caryclark@google.com495f8e42012-05-31 13:13:11 +00002493 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002494 // This may be called when the segment is already marked done. While this
2495 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002496 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002497 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002498 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002499 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002500 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002501 double referenceT = fTs[index].fT;
2502 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002503 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002504 markOneDone(__FUNCTION__, lesser, winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002505 }
2506 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002507 markOneDone(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002508 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002509 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002510
caryclark@google.com24bec792012-08-20 12:43:57 +00002511 void markOneDone(const char* funName, int tIndex, int winding) {
2512 Span* span = markOneWinding(funName, tIndex, winding);
2513 if (!span) {
2514 return;
2515 }
2516 span->fDone = true;
2517 fDoneSpans++;
2518 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002519
caryclark@google.com24bec792012-08-20 12:43:57 +00002520 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2521 Span& span = fTs[tIndex];
2522 if (span.fDone) {
2523 return NULL;
2524 }
2525 #if DEBUG_MARK_DONE
2526 debugShowNewWinding(funName, span, winding);
2527 #endif
2528 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002529 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002530 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002531 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002532 span.fWindSum = winding;
2533 return &span;
2534 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002535
caryclark@google.com59823f72012-08-09 18:17:47 +00002536 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002537 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002538 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002539 double referenceT = fTs[index].fT;
2540 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002541 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002542 markOneWinding(__FUNCTION__, lesser, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002543 }
2544 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002545 markOneWinding(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002546 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002547 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002548
caryclark@google.com2ddff932012-08-07 21:25:27 +00002549 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002550 int nextDoorWind = SK_MaxS32;
2551 if (tIndex > 0) {
2552 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002553 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002554 nextDoorWind = below.fWindValue;
2555 }
2556 }
2557 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2558 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002559 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002560 nextDoorWind = above.fWindValue;
2561 }
2562 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002563 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2564 const Span& below = fTs[tIndex - 1];
2565 nextDoorWind = below.fWindValue;
2566 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002567 if (nextDoorWind != SK_MaxS32) {
2568 Span& newSpan = fTs[tIndex];
2569 newSpan.fWindValue = nextDoorWind;
2570 if (!nextDoorWind) {
2571 newSpan.fDone = true;
2572 ++fDoneSpans;
2573 }
2574 }
2575 }
2576
caryclark@google.com9764cc62012-07-12 19:29:45 +00002577 // return span if when chasing, two or more radiating spans are not done
2578 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2579 // candidate and the remaining spans have windValue == 0 (canceled by
2580 // coincidence). The coincident edges could either be removed altogether,
2581 // or this code could be more complicated in detecting this case. Worth it?
2582 bool multipleSpans(int end) const {
2583 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002584 }
2585
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002586 // This has callers for two different situations: one establishes the end
2587 // of the current span, and one establishes the beginning of the next span
2588 // (thus the name). When this is looking for the end of the current span,
2589 // coincidence is found when the beginning Ts contain -step and the end
2590 // contains step. When it is looking for the beginning of the next, the
2591 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002592 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002593 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002594 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002595 int count = fTs.count();
2596 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002597 while (step > 0 ? ++to < count : --to >= 0) {
2598 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002599 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002600 continue;
2601 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002602 return to;
2603 }
2604 return -1;
2605 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002606
2607 bool operand() const {
2608 return fOperand;
2609 }
2610
2611 int oppSign(int startIndex, int endIndex) const {
2612 int result = startIndex < endIndex ? -fTs[startIndex].fWindValueOpp :
2613 fTs[endIndex].fWindValueOpp;
2614#if DEBUG_WIND_BUMP
2615 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2616#endif
2617 return result;
2618 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002619
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002620 const SkPoint* pts() const {
2621 return fPts;
2622 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002623
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002624 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002625 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002626 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2627 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002628 }
2629
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002630 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002631 const Span& span(int tIndex) const {
2632 return fTs[tIndex];
2633 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002634
caryclark@google.com235f56a2012-09-14 14:19:30 +00002635 int spanSign(const Angle* angle) const {
2636 SkASSERT(angle->segment() == this);
2637 return spanSign(angle->start(), angle->end());
2638 }
2639
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002640 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002641 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue :
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002642 fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002643#if DEBUG_WIND_BUMP
2644 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2645#endif
2646 return result;
2647 }
2648
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002649 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002650 double t(int tIndex) const {
2651 return fTs[tIndex].fT;
2652 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002653
caryclark@google.com18063442012-07-25 12:05:18 +00002654 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
2655 double start) {
2656 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002657 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00002658 *outsideTs.append() = end;
2659 *outsideTs.append() = start;
2660 }
2661 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002662
caryclark@google.com24bec792012-08-20 12:43:57 +00002663 void undoneSpan(int& start, int& end) {
2664 size_t tCount = fTs.count();
2665 size_t index;
2666 for (index = 0; index < tCount; ++index) {
2667 if (!fTs[index].fDone) {
2668 break;
2669 }
2670 }
2671 SkASSERT(index < tCount - 1);
2672 start = index;
2673 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002674 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00002675 SkASSERT(index < tCount);
2676 SkASSERT(index < tCount);
2677 end = index;
2678 }
caryclark@google.com18063442012-07-25 12:05:18 +00002679
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002680 void updatePts(const SkPoint pts[]) {
2681 fPts = pts;
2682 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002683
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002684 SkPath::Verb verb() const {
2685 return fVerb;
2686 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002687
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002688 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002689 return fTs[tIndex].fWindSum;
2690 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002691
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002692 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002693 int start = angle->start();
2694 int end = angle->end();
2695 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002696 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002697 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002698
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002699 int windValue(int tIndex) const {
2700 return fTs[tIndex].fWindValue;
2701 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002702
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002703 int windValue(const Angle* angle) const {
2704 int start = angle->start();
2705 int end = angle->end();
2706 int index = SkMin32(start, end);
2707 return windValue(index);
2708 }
2709
2710 SkScalar xAtT(const Span* span) const {
2711 return xyAtT(span).fX;
2712 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002713
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002714 const SkPoint& xyAtT(int index) const {
2715 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002716 }
2717
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002718 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002719 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002720 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002721 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002722 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002723 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002724 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002725 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002726 }
2727 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002728 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002729 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002730
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002731 SkScalar yAtT(int index) const {
2732 return yAtT(&fTs[index]);
2733 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002734
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002735 SkScalar yAtT(const Span* span) const {
2736 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002737 }
2738
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002739#if DEBUG_DUMP
2740 void dump() const {
2741 const char className[] = "Segment";
2742 const int tab = 4;
2743 for (int i = 0; i < fTs.count(); ++i) {
2744 SkPoint out;
2745 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
2746 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002747 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002748 tab + sizeof(className), className, fID,
2749 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002750 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002751 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002752 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002753 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00002754 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002755 }
2756#endif
2757
caryclark@google.com47580692012-07-23 12:14:49 +00002758#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00002759 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002760 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00002761 for (int i = 0; i < fTs.count(); ++i) {
2762 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
2763 return;
2764 }
2765 }
2766 SkASSERT(0);
2767 }
2768#endif
2769
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002770#if DEBUG_DUMP
2771 int debugID() const {
2772 return fID;
2773 }
2774#endif
2775
caryclark@google.com24bec792012-08-20 12:43:57 +00002776#if DEBUG_WINDING
2777 void debugShowSums() const {
2778 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
2779 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
2780 for (int i = 0; i < fTs.count(); ++i) {
2781 const Span& span = fTs[i];
2782 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
2783 if (span.fWindSum == SK_MinS32) {
2784 SkDebugf("?");
2785 } else {
2786 SkDebugf("%d", span.fWindSum);
2787 }
2788 SkDebugf("]");
2789 }
2790 SkDebugf("\n");
2791 }
2792#endif
2793
caryclark@google.comcc905052012-07-25 20:59:42 +00002794#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002795 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00002796 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00002797 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00002798 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00002799 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
2800 }
2801 SkDebugf("\n");
2802 }
2803#endif
2804
caryclark@google.com027de222012-07-12 12:52:50 +00002805#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002806 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00002807 if (done()) {
2808 return;
2809 }
2810 for (int i = 0; i < fTs.count(); ++i) {
2811 if (fTs[i].fDone) {
2812 continue;
2813 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002814 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00002815 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2816 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2817 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2818 }
2819 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00002820 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00002821 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00002822 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002823 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
2824 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
2825 if (fTs[i].fWindSum == SK_MinS32) {
2826 SkDebugf("?");
2827 } else {
2828 SkDebugf("%d", fTs[i].fWindSum);
2829 }
2830 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00002831 }
2832 }
2833#endif
2834
caryclark@google.com0c803d02012-08-06 11:15:47 +00002835#if DEBUG_MARK_DONE
2836 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
2837 const SkPoint& pt = xyAtT(&span);
2838 SkDebugf("%s id=%d", fun, fID);
2839 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2840 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2841 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2842 }
2843 SkDebugf(") t=%1.9g (%1.9g,%1.9g) newWindSum=%d windSum=",
2844 span.fT, pt.fX, pt.fY, winding);
2845 if (span.fWindSum == SK_MinS32) {
2846 SkDebugf("?");
2847 } else {
2848 SkDebugf("%d", span.fWindSum);
2849 }
2850 SkDebugf(" windValue=%d\n", span.fWindValue);
2851 }
2852#endif
2853
caryclark@google.com47580692012-07-23 12:14:49 +00002854#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002855 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002856 const int contourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002857 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00002858 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002859 int lastSum = contourWinding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002860 int windSum = lastSum - spanSign(angles[first]);
caryclark@google.com03f97062012-08-21 13:13:52 +00002861 SkDebugf("%s %s contourWinding=%d sign=%d\n", fun, __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +00002862 contourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00002863 int index = first;
2864 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00002865 do {
2866 const Angle& angle = *angles[index];
2867 const Segment& segment = *angle.segment();
2868 int start = angle.start();
2869 int end = angle.end();
2870 const Span& sSpan = segment.fTs[start];
2871 const Span& eSpan = segment.fTs[end];
2872 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002873 if (!firstTime) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002874 lastSum = windSum;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002875 windSum -= segment.spanSign(&angle);
caryclark@google.comafe56de2012-07-24 18:11:03 +00002876 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002877 SkDebugf("%s [%d] id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
caryclark@google.comc899ad92012-08-23 15:24:42 +00002878 " sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
2879 __FUNCTION__, index, segment.fID, kLVerbStr[segment.fVerb],
2880 start, segment.xAtT(&sSpan),
2881 segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
2882 segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
2883 lastSum, windSum, useInnerWinding(lastSum, windSum)
2884 ? windSum : lastSum, mSpan.fDone);
2885#if DEBUG_ANGLE
2886 angle.debugShow(segment.xyAtT(&sSpan));
2887#endif
caryclark@google.com47580692012-07-23 12:14:49 +00002888 ++index;
2889 if (index == angles.count()) {
2890 index = 0;
2891 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002892 if (firstTime) {
2893 firstTime = false;
2894 }
caryclark@google.com47580692012-07-23 12:14:49 +00002895 } while (index != first);
2896 }
2897#endif
2898
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002899#if DEBUG_WINDING
2900 bool debugVerifyWinding(int start, int end, int winding) const {
2901 const Span& span = fTs[SkMin32(start, end)];
2902 int spanWinding = span.fWindSum;
2903 if (spanWinding == SK_MinS32) {
2904 return true;
2905 }
2906 int spanSign = SkSign32(start - end);
2907 int signedVal = spanSign * span.fWindValue;
2908 if (signedVal < 0) {
2909 spanWinding -= signedVal;
2910 }
2911 return span.fWindSum == winding;
2912 }
2913#endif
2914
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002915private:
2916 const SkPoint* fPts;
2917 SkPath::Verb fVerb;
2918 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002919 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00002920 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00002921 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002922#if DEBUG_DUMP
2923 int fID;
2924#endif
2925};
2926
caryclark@google.comb9738012012-07-03 19:53:30 +00002927class Contour;
2928
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002929struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00002930 Contour* fContours[2];
2931 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002932 double fTs[2][2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00002933 bool fXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002934};
2935
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002936class Contour {
2937public:
2938 Contour() {
2939 reset();
2940#if DEBUG_DUMP
2941 fID = ++gContourID;
2942#endif
2943 }
2944
2945 bool operator<(const Contour& rh) const {
2946 return fBounds.fTop == rh.fBounds.fTop
2947 ? fBounds.fLeft < rh.fBounds.fLeft
2948 : fBounds.fTop < rh.fBounds.fTop;
2949 }
2950
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002951 void addCoincident(int index, Contour* other, int otherIndex,
2952 const Intersections& ts, bool swap) {
2953 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00002954 coincidence.fContours[0] = this;
2955 coincidence.fContours[1] = other;
2956 coincidence.fSegments[0] = index;
2957 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00002958 if (fSegments[index].verb() == SkPath::kLine_Verb &&
2959 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
2960 // FIXME: coincident lines use legacy Ts instead of coincident Ts
2961 coincidence.fTs[swap][0] = ts.fT[0][0];
2962 coincidence.fTs[swap][1] = ts.fT[0][1];
2963 coincidence.fTs[!swap][0] = ts.fT[1][0];
2964 coincidence.fTs[!swap][1] = ts.fT[1][1];
2965 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
2966 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
2967 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
2968 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
2969 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
2970 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
2971 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002972 coincidence.fXor = fOperand == other->fOperand ? fXor : true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002973 }
2974
2975 void addCross(const Contour* crosser) {
2976#ifdef DEBUG_CROSS
2977 for (int index = 0; index < fCrosses.count(); ++index) {
2978 SkASSERT(fCrosses[index] != crosser);
2979 }
2980#endif
2981 *fCrosses.append() = crosser;
2982 }
2983
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002984 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002985 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002986 fContainsCurves = true;
2987 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002988
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002989 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002990 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002991 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002992 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002993
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002994 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
2995 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
2996 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002997
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002998 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002999 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003000 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003001 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003002 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003003
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003004 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3005 containsIntercepts();
3006 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3007 }
3008
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003009 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003010 return fBounds;
3011 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003012
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003013 void complete() {
3014 setBounds();
3015 fContainsIntercepts = false;
3016 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003017
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003018 void containsIntercepts() {
3019 fContainsIntercepts = true;
3020 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003021
rmistry@google.comd6176b02012-08-23 18:14:13 +00003022 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003023 int &tIndex, double& hitT) {
3024 int segmentCount = fSegments.count();
3025 const Segment* bestSegment = NULL;
3026 for (int test = 0; test < segmentCount; ++test) {
3027 Segment* testSegment = &fSegments[test];
3028 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003029 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003030 continue;
3031 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003032 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003033 continue;
3034 }
3035 if (bounds.fLeft > basePt.fX) {
3036 continue;
3037 }
3038 if (bounds.fRight < basePt.fX) {
3039 continue;
3040 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003041 if (bounds.fLeft == bounds.fRight) {
3042 continue;
3043 }
3044 #if 0
3045 bool leftHalf = bounds.fLeft == basePt.fX;
3046 bool rightHalf = bounds.fRight == basePt.fX;
3047 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3048 basePt, leftHalf, rightHalf)) {
3049 continue;
3050 }
3051 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003052 double testHitT;
3053 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3054 if (testT >= 0) {
3055 bestSegment = testSegment;
3056 tIndex = testT;
3057 hitT = testHitT;
3058 }
3059 }
3060 return bestSegment;
3061 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003062
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003063 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003064 for (int index = 0; index < fCrosses.count(); ++index) {
3065 if (fCrosses[index] == crosser) {
3066 return true;
3067 }
3068 }
3069 return false;
3070 }
3071
caryclark@google.com235f56a2012-09-14 14:19:30 +00003072 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003073 int segmentCount = fSegments.count();
3074 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003075 fSegments[sIndex].findTooCloseToCall(fXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003076 }
3077 }
3078
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003079 void fixOtherTIndex() {
3080 int segmentCount = fSegments.count();
3081 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3082 fSegments[sIndex].fixOtherTIndex();
3083 }
3084 }
3085
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003086 void reset() {
3087 fSegments.reset();
3088 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003089 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003090 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003091
caryclark@google.com235f56a2012-09-14 14:19:30 +00003092 // FIXME: for binary ops, need to keep both ops winding contributions separately
3093 // in edge array
3094 void resolveCoincidence() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003095 int count = fCoincidences.count();
3096 for (int index = 0; index < count; ++index) {
3097 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003098 Contour* thisContour = coincidence.fContours[0];
3099 Contour* otherContour = coincidence.fContours[1];
3100 int thisIndex = coincidence.fSegments[0];
3101 int otherIndex = coincidence.fSegments[1];
3102 Segment& thisOne = thisContour->fSegments[thisIndex];
3103 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003104 #if DEBUG_CONCIDENT
3105 thisOne.debugShowTs();
3106 other.debugShowTs();
3107 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003108 double startT = coincidence.fTs[0][0];
3109 double endT = coincidence.fTs[0][1];
caryclark@google.com32546db2012-08-31 20:55:07 +00003110 bool opposite = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003111 if (startT > endT) {
3112 SkTSwap<double>(startT, endT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003113 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003114 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003115 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003116 double oStartT = coincidence.fTs[1][0];
3117 double oEndT = coincidence.fTs[1][1];
3118 if (oStartT > oEndT) {
3119 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003120 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003121 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003122 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com32546db2012-08-31 20:55:07 +00003123 if (opposite) {
caryclark@google.comb9738012012-07-03 19:53:30 +00003124 // make sure startT and endT have t entries
caryclark@google.com32546db2012-08-31 20:55:07 +00003125 SkASSERT(opposite);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003126 if (startT > 0 || oEndT < 1
3127 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3128 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003129 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003130 if (oStartT > 0 || endT < 1
3131 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3132 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003133 }
3134 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003135 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003136 if (startT > 0 || oStartT > 0
3137 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003138 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003139 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003140 if (endT < 1 || oEndT < 1
3141 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003142 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003143 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003144 thisOne.addTCoincident(coincidence.fXor, startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003145 }
caryclark@google.com47580692012-07-23 12:14:49 +00003146 #if DEBUG_CONCIDENT
3147 thisOne.debugShowTs();
3148 other.debugShowTs();
3149 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003150 }
3151 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003152
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003153 const SkTArray<Segment>& segments() {
3154 return fSegments;
3155 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003156
caryclark@google.com235f56a2012-09-14 14:19:30 +00003157 void setOperand(bool isOp) {
3158 fOperand = isOp;
3159 }
3160
3161 void setXor(bool isXor) {
3162 fXor = isXor;
3163 }
3164
caryclark@google.com15fa1382012-05-07 20:49:36 +00003165 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3166 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003167 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003168 // segments' top, and not a true sort, so it could be ameniable to regular
3169 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003170 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003171 int segmentCount = fSegments.count();
3172 SkASSERT(segmentCount > 0);
3173 int best = -1;
3174 Segment* bestSegment = NULL;
3175 while (++best < segmentCount) {
3176 Segment* testSegment = &fSegments[best];
3177 if (testSegment->done()) {
3178 continue;
3179 }
3180 bestSegment = testSegment;
3181 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003182 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003183 if (!bestSegment) {
3184 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003185 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003186 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003187 for (int test = best + 1; test < segmentCount; ++test) {
3188 Segment* testSegment = &fSegments[test];
3189 if (testSegment->done()) {
3190 continue;
3191 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003192 if (testSegment->bounds().fTop > bestTop) {
3193 continue;
3194 }
3195 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003196 if (bestTop > testTop) {
3197 bestTop = testTop;
3198 bestSegment = testSegment;
3199 }
3200 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003201 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003202 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003203 }
3204
caryclark@google.com24bec792012-08-20 12:43:57 +00003205 Segment* undoneSegment(int& start, int& end) {
3206 int segmentCount = fSegments.count();
3207 for (int test = 0; test < segmentCount; ++test) {
3208 Segment* testSegment = &fSegments[test];
3209 if (testSegment->done()) {
3210 continue;
3211 }
3212 testSegment->undoneSpan(start, end);
3213 return testSegment;
3214 }
3215 return NULL;
3216 }
3217
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003218 int updateSegment(int index, const SkPoint* pts) {
3219 Segment& segment = fSegments[index];
3220 segment.updatePts(pts);
3221 return segment.verb() + 1;
3222 }
3223
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003224#if DEBUG_TEST
3225 SkTArray<Segment>& debugSegments() {
3226 return fSegments;
3227 }
3228#endif
3229
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003230#if DEBUG_DUMP
3231 void dump() {
3232 int i;
3233 const char className[] = "Contour";
3234 const int tab = 4;
3235 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
3236 for (i = 0; i < fSegments.count(); ++i) {
3237 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
3238 className, i);
3239 fSegments[i].dump();
3240 }
3241 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
3242 tab + sizeof(className), className,
3243 fBounds.fLeft, fBounds.fTop,
3244 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003245 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
3246 className, fContainsIntercepts);
3247 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
3248 className, fContainsCurves);
3249 }
3250#endif
3251
caryclark@google.com027de222012-07-12 12:52:50 +00003252#if DEBUG_ACTIVE_SPANS
3253 void debugShowActiveSpans() {
3254 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003255 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00003256 }
3257 }
3258#endif
3259
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003260protected:
3261 void setBounds() {
3262 int count = fSegments.count();
3263 if (count == 0) {
3264 SkDebugf("%s empty contour\n", __FUNCTION__);
3265 SkASSERT(0);
3266 // FIXME: delete empty contour?
3267 return;
3268 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003269 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003270 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003271 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003272 }
3273 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003274
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003275private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003276 SkTArray<Segment> fSegments;
3277 SkTDArray<Coincidence> fCoincidences;
3278 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003279 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003280 bool fContainsIntercepts;
3281 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003282 bool fOperand; // true for the second argument to a binary operator
3283 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003284#if DEBUG_DUMP
3285 int fID;
3286#endif
3287};
3288
3289class EdgeBuilder {
3290public:
3291
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003292EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003293 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003294 , fContours(contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003295 , fCurrentContour(NULL)
3296 , fOperand(false)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003297{
caryclark@google.com235f56a2012-09-14 14:19:30 +00003298 fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003299#if DEBUG_DUMP
3300 gContourID = 0;
3301 gSegmentID = 0;
3302#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00003303 fSecondHalf = preFetch();
3304}
3305
3306void addOperand(const SkPath& path) {
3307 fPath = &path;
3308 fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
3309 preFetch();
3310}
3311
3312void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003313 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003314 complete();
3315 if (fCurrentContour && !fCurrentContour->segments().count()) {
3316 fContours.pop_back();
3317 }
3318 // correct pointers in contours since fReducePts may have moved as it grew
3319 int cIndex = 0;
3320 int extraCount = fExtra.count();
3321 SkASSERT(extraCount == 0 || fExtra[0] == -1);
3322 int eIndex = 0;
3323 int rIndex = 0;
3324 while (++eIndex < extraCount) {
3325 int offset = fExtra[eIndex];
3326 if (offset < 0) {
3327 ++cIndex;
3328 continue;
3329 }
3330 fCurrentContour = &fContours[cIndex];
3331 rIndex += fCurrentContour->updateSegment(offset - 1,
3332 &fReducePts[rIndex]);
3333 }
3334 fExtra.reset(); // we're done with this
3335}
3336
3337ShapeOpMask xorMask() const {
3338 return fXorMask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003339}
3340
3341protected:
3342
3343void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003344 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003345 fCurrentContour->complete();
3346 fCurrentContour = NULL;
3347 }
3348}
3349
caryclark@google.com235f56a2012-09-14 14:19:30 +00003350// FIXME:remove once we can access path pts directly
3351int preFetch() {
3352 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003353 SkPoint pts[4];
3354 SkPath::Verb verb;
3355 do {
3356 verb = iter.next(pts);
3357 *fPathVerbs.append() = verb;
3358 if (verb == SkPath::kMove_Verb) {
3359 *fPathPts.append() = pts[0];
3360 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
3361 fPathPts.append(verb, &pts[1]);
3362 }
3363 } while (verb != SkPath::kDone_Verb);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003364 return fPathVerbs.count();
3365}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003366
caryclark@google.com235f56a2012-09-14 14:19:30 +00003367void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003368 SkPath::Verb reducedVerb;
3369 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003370 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003371 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003372 const SkPoint* finalCurveStart = NULL;
3373 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003374 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003375 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
3376 switch (verb) {
3377 case SkPath::kMove_Verb:
3378 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003379 if (!fCurrentContour) {
3380 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003381 fCurrentContour->setOperand(fOperand);
3382 fCurrentContour->setXor(fXorMask == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003383 *fExtra.append() = -1; // start new contour
3384 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003385 finalCurveEnd = pointsPtr++;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003386 continue;
3387 case SkPath::kLine_Verb:
3388 // skip degenerate points
3389 if (pointsPtr[-1].fX != pointsPtr[0].fX
3390 || pointsPtr[-1].fY != pointsPtr[0].fY) {
3391 fCurrentContour->addLine(&pointsPtr[-1]);
3392 }
3393 break;
3394 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00003395
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003396 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
3397 if (reducedVerb == 0) {
3398 break; // skip degenerate points
3399 }
3400 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00003401 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003402 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003403 break;
3404 }
3405 fCurrentContour->addQuad(&pointsPtr[-1]);
3406 break;
3407 case SkPath::kCubic_Verb:
3408 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
3409 if (reducedVerb == 0) {
3410 break; // skip degenerate points
3411 }
3412 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003413 *fExtra.append() =
3414 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003415 break;
3416 }
3417 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003418 *fExtra.append() =
3419 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003420 break;
3421 }
3422 fCurrentContour->addCubic(&pointsPtr[-1]);
3423 break;
3424 case SkPath::kClose_Verb:
3425 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003426 if (finalCurveStart && finalCurveEnd
3427 && *finalCurveStart != *finalCurveEnd) {
3428 *fReducePts.append() = *finalCurveStart;
3429 *fReducePts.append() = *finalCurveEnd;
3430 *fExtra.append() =
3431 fCurrentContour->addLine(fReducePts.end() - 2);
3432 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003433 complete();
3434 continue;
3435 default:
3436 SkDEBUGFAIL("bad verb");
3437 return;
3438 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003439 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003440 pointsPtr += verb;
3441 SkASSERT(fCurrentContour);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003442 if (verbPtr == endOfFirstHalf) {
3443 fOperand = true;
3444 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003445 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003446}
3447
3448private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00003449 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003450 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003451 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003452 Contour* fCurrentContour;
3453 SkTArray<Contour>& fContours;
3454 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003455 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com235f56a2012-09-14 14:19:30 +00003456 ShapeOpMask fXorMask;
3457 int fSecondHalf;
3458 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003459};
3460
3461class Work {
3462public:
3463 enum SegmentType {
3464 kHorizontalLine_Segment = -1,
3465 kVerticalLine_Segment = 0,
3466 kLine_Segment = SkPath::kLine_Verb,
3467 kQuad_Segment = SkPath::kQuad_Verb,
3468 kCubic_Segment = SkPath::kCubic_Verb,
3469 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00003470
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003471 void addCoincident(Work& other, const Intersections& ts, bool swap) {
3472 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
3473 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003474
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003475 // FIXME: does it make sense to write otherIndex now if we're going to
3476 // fix it up later?
3477 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003478 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003479 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003480
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003481 // Avoid collapsing t values that are close to the same since
3482 // we walk ts to describe consecutive intersections. Since a pair of ts can
3483 // be nearly equal, any problems caused by this should be taken care
3484 // of later.
3485 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003486 int addT(double newT, const Work& other) {
3487 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003488 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003489
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003490 bool advance() {
3491 return ++fIndex < fLast;
3492 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003493
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003494 SkScalar bottom() const {
3495 return bounds().fBottom;
3496 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003497
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003498 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003499 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003500 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003501
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003502 const SkPoint* cubic() const {
3503 return fCubic;
3504 }
3505
3506 void init(Contour* contour) {
3507 fContour = contour;
3508 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003509 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003510 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003511
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003512 bool isAdjacent(const Work& next) {
3513 return fContour == next.fContour && fIndex + 1 == next.fIndex;
3514 }
3515
3516 bool isFirstLast(const Work& next) {
3517 return fContour == next.fContour && fIndex == 0
3518 && next.fIndex == fLast - 1;
3519 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003520
3521 SkScalar left() const {
3522 return bounds().fLeft;
3523 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003524
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003525 void promoteToCubic() {
3526 fCubic[0] = pts()[0];
3527 fCubic[2] = pts()[1];
3528 fCubic[3] = pts()[2];
3529 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
3530 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
3531 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
3532 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
3533 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003534
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003535 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003536 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003537 }
3538
3539 SkScalar right() const {
3540 return bounds().fRight;
3541 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003542
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003543 ptrdiff_t segmentIndex() const {
3544 return fIndex;
3545 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003546
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003547 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003548 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003549 SegmentType type = (SegmentType) segment.verb();
3550 if (type != kLine_Segment) {
3551 return type;
3552 }
3553 if (segment.isHorizontal()) {
3554 return kHorizontalLine_Segment;
3555 }
3556 if (segment.isVertical()) {
3557 return kVerticalLine_Segment;
3558 }
3559 return kLine_Segment;
3560 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003561
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003562 bool startAfter(const Work& after) {
3563 fIndex = after.fIndex;
3564 return advance();
3565 }
3566
3567 SkScalar top() const {
3568 return bounds().fTop;
3569 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003570
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003571 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003572 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003573 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003574
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003575 SkScalar x() const {
3576 return bounds().fLeft;
3577 }
3578
3579 bool xFlipped() const {
3580 return x() != pts()[0].fX;
3581 }
3582
3583 SkScalar y() const {
3584 return bounds().fTop;
3585 }
3586
3587 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003588 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003589 }
3590
3591protected:
3592 Contour* fContour;
3593 SkPoint fCubic[4];
3594 int fIndex;
3595 int fLast;
3596};
3597
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003598#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003599static void debugShowLineIntersection(int pts, const Work& wt,
3600 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003601 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003602 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
3603 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
3604 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
3605 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003606 return;
3607 }
3608 SkPoint wtOutPt, wnOutPt;
3609 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3610 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003611 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003612 __FUNCTION__,
3613 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3614 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
3615 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003616 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003617 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003618 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003619 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3620 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
3621 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003622 SkDebugf(" wnTs[1]=%g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003623 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003624 SkDebugf("\n");
3625}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003626#else
3627static void debugShowLineIntersection(int , const Work& ,
3628 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003629}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003630#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003631
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003632static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003633
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003634 if (test != next) {
3635 if (test->bounds().fBottom < next->bounds().fTop) {
3636 return false;
3637 }
3638 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
3639 return true;
3640 }
3641 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003642 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003643 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003644 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003645 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003646 Work wn;
3647 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003648 if (test == next && !wn.startAfter(wt)) {
3649 continue;
3650 }
3651 do {
3652 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
3653 continue;
3654 }
3655 int pts;
3656 Intersections ts;
3657 bool swap = false;
3658 switch (wt.segmentType()) {
3659 case Work::kHorizontalLine_Segment:
3660 swap = true;
3661 switch (wn.segmentType()) {
3662 case Work::kHorizontalLine_Segment:
3663 case Work::kVerticalLine_Segment:
3664 case Work::kLine_Segment: {
3665 pts = HLineIntersect(wn.pts(), wt.left(),
3666 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003667 debugShowLineIntersection(pts, wt, wn,
3668 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003669 break;
3670 }
3671 case Work::kQuad_Segment: {
3672 pts = HQuadIntersect(wn.pts(), wt.left(),
3673 wt.right(), wt.y(), wt.xFlipped(), ts);
3674 break;
3675 }
3676 case Work::kCubic_Segment: {
3677 pts = HCubicIntersect(wn.pts(), wt.left(),
3678 wt.right(), wt.y(), wt.xFlipped(), ts);
3679 break;
3680 }
3681 default:
3682 SkASSERT(0);
3683 }
3684 break;
3685 case Work::kVerticalLine_Segment:
3686 swap = true;
3687 switch (wn.segmentType()) {
3688 case Work::kHorizontalLine_Segment:
3689 case Work::kVerticalLine_Segment:
3690 case Work::kLine_Segment: {
3691 pts = VLineIntersect(wn.pts(), wt.top(),
3692 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003693 debugShowLineIntersection(pts, wt, wn,
3694 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003695 break;
3696 }
3697 case Work::kQuad_Segment: {
3698 pts = VQuadIntersect(wn.pts(), wt.top(),
3699 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3700 break;
3701 }
3702 case Work::kCubic_Segment: {
3703 pts = VCubicIntersect(wn.pts(), wt.top(),
3704 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3705 break;
3706 }
3707 default:
3708 SkASSERT(0);
3709 }
3710 break;
3711 case Work::kLine_Segment:
3712 switch (wn.segmentType()) {
3713 case Work::kHorizontalLine_Segment:
3714 pts = HLineIntersect(wt.pts(), wn.left(),
3715 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003716 debugShowLineIntersection(pts, wt, wn,
3717 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003718 break;
3719 case Work::kVerticalLine_Segment:
3720 pts = VLineIntersect(wt.pts(), wn.top(),
3721 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003722 debugShowLineIntersection(pts, wt, wn,
3723 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003724 break;
3725 case Work::kLine_Segment: {
3726 pts = LineIntersect(wt.pts(), wn.pts(), ts);
3727 debugShowLineIntersection(pts, wt, wn,
3728 ts.fT[1], ts.fT[0]);
3729 break;
3730 }
3731 case Work::kQuad_Segment: {
3732 swap = true;
3733 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
3734 break;
3735 }
3736 case Work::kCubic_Segment: {
3737 swap = true;
3738 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
3739 break;
3740 }
3741 default:
3742 SkASSERT(0);
3743 }
3744 break;
3745 case Work::kQuad_Segment:
3746 switch (wn.segmentType()) {
3747 case Work::kHorizontalLine_Segment:
3748 pts = HQuadIntersect(wt.pts(), wn.left(),
3749 wn.right(), wn.y(), wn.xFlipped(), ts);
3750 break;
3751 case Work::kVerticalLine_Segment:
3752 pts = VQuadIntersect(wt.pts(), wn.top(),
3753 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3754 break;
3755 case Work::kLine_Segment: {
3756 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
3757 break;
3758 }
3759 case Work::kQuad_Segment: {
3760 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
3761 break;
3762 }
3763 case Work::kCubic_Segment: {
3764 wt.promoteToCubic();
3765 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
3766 break;
3767 }
3768 default:
3769 SkASSERT(0);
3770 }
3771 break;
3772 case Work::kCubic_Segment:
3773 switch (wn.segmentType()) {
3774 case Work::kHorizontalLine_Segment:
3775 pts = HCubicIntersect(wt.pts(), wn.left(),
3776 wn.right(), wn.y(), wn.xFlipped(), ts);
3777 break;
3778 case Work::kVerticalLine_Segment:
3779 pts = VCubicIntersect(wt.pts(), wn.top(),
3780 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3781 break;
3782 case Work::kLine_Segment: {
3783 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
3784 break;
3785 }
3786 case Work::kQuad_Segment: {
3787 wn.promoteToCubic();
3788 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
3789 break;
3790 }
3791 case Work::kCubic_Segment: {
3792 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
3793 break;
3794 }
3795 default:
3796 SkASSERT(0);
3797 }
3798 break;
3799 default:
3800 SkASSERT(0);
3801 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003802 if (!foundCommonContour && pts > 0) {
3803 test->addCross(next);
3804 next->addCross(test);
3805 foundCommonContour = true;
3806 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003807 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00003808 if (pts == 2) {
3809 if (wn.segmentType() <= Work::kLine_Segment
3810 && wt.segmentType() <= Work::kLine_Segment) {
3811 wt.addCoincident(wn, ts, swap);
3812 continue;
3813 }
3814 if (wn.segmentType() == Work::kQuad_Segment
3815 && wt.segmentType() == Work::kQuad_Segment
3816 && ts.coincidentUsed() == 2) {
3817 wt.addCoincident(wn, ts, swap);
3818 continue;
3819 }
3820
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003821 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003822 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003823 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
3824 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003825 int testTAt = wt.addT(ts.fT[swap][pt], wn);
3826 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003827 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
3828 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003829 }
3830 } while (wn.advance());
3831 } while (wt.advance());
3832 return true;
3833}
3834
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003835// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003836// see if coincidence is formed by clipping non-concident segments
caryclark@google.com235f56a2012-09-14 14:19:30 +00003837static void coincidenceCheck(SkTDArray<Contour*>& contourList) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003838 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00003839 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003840 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00003841 contour->resolveCoincidence();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003842 }
3843 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
3844 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00003845 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003846 }
3847}
3848
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003849// project a ray from the top of the contour up and see if it hits anything
3850// note: when we compute line intersections, we keep track of whether
3851// two contours touch, so we need only look at contours not touching this one.
3852// OPTIMIZATION: sort contourList vertically to avoid linear walk
3853static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003854 const Segment* current, int index, int endIndex) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003855 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003856 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003857 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00003858 const Segment* test = NULL;
3859 int tIndex;
3860 double tHit;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003861 // bool checkCrosses = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003862 for (int cTest = 0; cTest < contourCount; ++cTest) {
3863 Contour* contour = contourList[cTest];
3864 if (basePt.fY < contour->bounds().fTop) {
3865 continue;
3866 }
3867 if (bestY > contour->bounds().fBottom) {
3868 continue;
3869 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003870#if 0
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003871 // even though the contours crossed, if spans cancel through concidence,
3872 // the contours may be not have any span links to chase, and the current
3873 // segment may be isolated. Detect this by seeing if current has
3874 // uninitialized wind sums. If so, project a ray instead of relying on
3875 // previously found intersections.
3876 if (baseContour == contour) {
3877 continue;
3878 }
3879 if (checkCrosses && baseContour->crosses(contour)) {
3880 if (current->isConnected(index, endIndex)) {
3881 continue;
3882 }
3883 checkCrosses = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003884 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003885#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003886 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
3887 if (next) {
3888 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003889 }
caryclark@google.com47580692012-07-23 12:14:49 +00003890 }
3891 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00003892 return 0;
3893 }
3894 int winding, windValue;
3895 // If the ray hit the end of a span, we need to construct the wheel of
3896 // angles to find the span closest to the ray -- even if there are just
3897 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003898 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003899 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00003900 SkTDArray<Angle> angles;
3901 int end = test->nextSpan(tIndex, 1);
3902 if (end < 0) {
3903 end = test->nextSpan(tIndex, -1);
3904 }
3905 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00003906 SkASSERT(angles.count() > 0);
3907 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
3908#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00003909 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00003910#endif
3911 return 0;
3912 }
caryclark@google.com47580692012-07-23 12:14:49 +00003913 test->buildAngles(tIndex, angles);
3914 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00003915 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00003916 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00003917 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00003918 // hour after 6 o'clock
3919 sortAngles(angles, sorted);
3920#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00003921 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00003922#endif
3923 // walk the sorted angle fan to find the lowest angle
3924 // above the base point. Currently, the first angle in the sorted array
3925 // is 12 noon or an earlier hour (the next counterclockwise)
3926 int count = sorted.count();
3927 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003928 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00003929 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003930 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00003931 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00003932 angle = sorted[index];
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003933 if (baseMatches && angle->isHorizontal()) {
3934 continue;
3935 }
3936 double indexDx = angle->dx();
caryclark@google.com47580692012-07-23 12:14:49 +00003937 if (indexDx < 0) {
3938 left = index;
3939 } else if (indexDx > 0) {
3940 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00003941 int previous = index - 1;
3942 if (previous < 0) {
3943 previous = count - 1;
3944 }
3945 const Angle* prev = sorted[previous];
3946 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
3947#if DEBUG_SORT
3948 SkDebugf("%s use prev\n", __FUNCTION__);
3949#endif
3950 right = previous;
3951 }
caryclark@google.com47580692012-07-23 12:14:49 +00003952 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003953 } else {
3954 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003955 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003956 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003957 if (left < 0 && right < 0) {
3958 left = mid;
3959 }
caryclark@google.com47580692012-07-23 12:14:49 +00003960 SkASSERT(left >= 0 || right >= 0);
3961 if (left < 0) {
3962 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003963 } else if (left >= 0 && mid >= 0 && right >= 0
3964 && sorted[mid]->sign() == sorted[right]->sign()) {
3965 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003966 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003967 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00003968 test = angle->segment();
3969 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00003970 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003971 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00003972#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003973 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
3974 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00003975#endif
3976 } else {
3977 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00003978 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003979 windValue = test->windValue(tIndex);
3980#if DEBUG_WINDING
3981 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
3982 windValue);
3983#endif
3984 }
3985 // see if a + change in T results in a +/- change in X (compute x'(T))
3986 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
3987#if DEBUG_WINDING
3988 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
3989#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00003990 SkASSERT(dx != 0);
3991 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00003992 winding += dx > 0 ? -windValue : windValue;
3993#if DEBUG_WINDING
3994 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
3995#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003996 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003997 return winding;
3998}
rmistry@google.comd6176b02012-08-23 18:14:13 +00003999
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004000// OPTIMIZATION: not crazy about linear search here to find top active y.
4001// seems like we should break down and do the sort, or maybe sort each
rmistry@google.comd6176b02012-08-23 18:14:13 +00004002// contours' segments?
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004003// Once the segment array is built, there's no reason I can think of not to
4004// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004005// FIXME: return the contour found to pass to inner contour check
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004006static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004007 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004008 int cIndex = 0;
4009 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004010 SkScalar bestY = SK_ScalarMax;
4011 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004012 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004013 contour = contourList[cIndex];
4014 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004015 } while (!topStart && ++cIndex < contourCount);
4016 if (!topStart) {
4017 return NULL;
4018 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004019 while (++cIndex < contourCount) {
4020 contour = contourList[cIndex];
4021 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004022 continue;
4023 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004024 SkScalar testY = SK_ScalarMax;
4025 Segment* test = contour->topSegment(testY);
4026 if (!test || bestY <= testY) {
4027 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004028 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004029 topStart = test;
4030 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004031 }
4032 return topStart;
4033}
4034
caryclark@google.com24bec792012-08-20 12:43:57 +00004035static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4036 int contourCount = contourList.count();
4037 Segment* result;
4038 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4039 Contour* contour = contourList[cIndex];
4040 result = contour->undoneSegment(start, end);
4041 if (result) {
4042 return result;
4043 }
4044 }
4045 return NULL;
4046}
4047
4048
4049
caryclark@google.come21cb182012-07-23 21:26:31 +00004050static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
4051 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004052 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004053 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004054 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4055 Segment* segment = backPtr.fOther;
4056 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004057 SkTDArray<Angle> angles;
4058 int done = 0;
4059 if (segment->activeAngle(tIndex, done, angles)) {
4060 Angle* last = angles.end() - 1;
4061 tIndex = last->start();
4062 endIndex = last->end();
4063 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004064 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004065 if (done == angles.count()) {
4066 chase.pop(&span);
4067 continue;
4068 }
4069 SkTDArray<Angle*> sorted;
4070 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004071#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004072 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004073#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004074 // find first angle, initialize winding to computed fWindSum
4075 int firstIndex = -1;
4076 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004077 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004078 do {
4079 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004080 segment = angle->segment();
4081 winding = segment->windSum(angle);
4082 } while (winding == SK_MinS32);
4083 int spanWinding = segment->spanSign(angle->start(), angle->end());
4084 #if DEBUG_WINDING
4085 SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
4086 __FUNCTION__, winding, spanWinding, contourWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00004087 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004088 // turn swinding into contourWinding
4089 if (spanWinding * winding < 0) {
4090 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004091 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004092 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004093 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004094 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004095 // we care about first sign and whether wind sum indicates this
4096 // edge is inside or outside. Maybe need to pass span winding
4097 // or first winding or something into this function?
4098 // advance to first undone angle, then return it and winding
4099 // (to set whether edges are active or not)
4100 int nextIndex = firstIndex + 1;
4101 int angleCount = sorted.count();
4102 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004103 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00004104 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00004105 do {
4106 SkASSERT(nextIndex != firstIndex);
4107 if (nextIndex == angleCount) {
4108 nextIndex = 0;
4109 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004110 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00004111 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004112 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00004113 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004114 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00004115 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
4116 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004117 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004118 tIndex = angle->start();
4119 endIndex = angle->end();
4120 int lesser = SkMin32(tIndex, endIndex);
4121 const Span& nextSpan = segment->span(lesser);
4122 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004123#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00004124 // FIXME: this be wrong. assign startWinding if edge is in
4125 // same direction. If the direction is opposite, winding to
4126 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00004127 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004128 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004129 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004130 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004131#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004132 break;
4133 }
4134 } while (++nextIndex != lastIndex);
4135 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004136 }
4137 return NULL;
4138}
4139
caryclark@google.com027de222012-07-12 12:52:50 +00004140#if DEBUG_ACTIVE_SPANS
4141static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
4142 for (int index = 0; index < contourList.count(); ++ index) {
4143 contourList[index]->debugShowActiveSpans();
4144 }
4145}
4146#endif
4147
caryclark@google.com27c449a2012-07-27 18:26:38 +00004148static bool windingIsActive(int winding, int spanWinding) {
4149 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
4150 && (!winding || !spanWinding || winding == -spanWinding);
4151}
4152
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004153// Each segment may have an inside or an outside. Segments contained within
4154// winding may have insides on either side, and form a contour that should be
4155// ignored. Segments that are coincident with opposing direction segments may
4156// have outsides on either side, and should also disappear.
4157// 'Normal' segments will have one inside and one outside. Subsequent connections
4158// when winding should follow the intersection direction. If more than one edge
4159// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004160 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00004161 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com24bec792012-08-20 12:43:57 +00004162static void bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004163 bool firstContour = true;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004164 do {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004165 Segment* topStart = findTopContour(contourList);
caryclark@google.com15fa1382012-05-07 20:49:36 +00004166 if (!topStart) {
4167 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00004168 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004169 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00004170 // follow edges to intersection by changing the index by direction.
4171 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004172 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004173 int contourWinding;
4174 if (firstContour) {
4175 contourWinding = 0;
4176 firstContour = false;
4177 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004178 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004179 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00004180 if (sumWinding == SK_MinS32) {
4181 sumWinding = current->computeSum(index, endIndex);
4182 }
4183 if (sumWinding == SK_MinS32) {
4184 contourWinding = innerContourCheck(contourList, current,
4185 index, endIndex);
4186 } else {
4187 contourWinding = sumWinding;
4188 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004189 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
4190 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00004191 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004192 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004193#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004194 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004195 sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00004196 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004197#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004198 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004199#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004200 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004201 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
4202#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004203 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004204 SkPoint lastPt;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004205 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004206 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004207 // FIXME: needs work. While it works in limited situations, it does
4208 // not always compute winding correctly. Active should be removed and instead
4209 // the initial winding should be correctly passed in so that if the
4210 // inner contour is wound the same way, it never finds an accumulated
4211 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00004212 // other than zero when resolving sorted angles.
caryclark@google.com27c449a2012-07-27 18:26:38 +00004213 bool active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004214 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004215 do {
caryclark@google.com0e08a192012-07-13 21:07:52 +00004216 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00004217 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
4218 __FUNCTION__, active ? "true" : "false",
4219 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004220 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004221 const SkPoint* firstPt = NULL;
4222 do {
4223 SkASSERT(!current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004224 int nextStart = index;
4225 int nextEnd = endIndex;
4226 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.com27c449a2012-07-27 18:26:38 +00004227 nextStart, nextEnd, winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004228 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00004229 if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
4230 lastPt = current->addCurveTo(index, endIndex, simple, true);
4231 SkASSERT(*firstPt == lastPt);
4232 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004233 break;
4234 }
4235 if (!firstPt) {
4236 firstPt = &current->addMoveTo(index, simple, active);
4237 }
4238 lastPt = current->addCurveTo(index, endIndex, simple, active);
4239 current = next;
4240 index = nextStart;
4241 endIndex = nextEnd;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004242 } while (*firstPt != lastPt && (active || !current->done()));
4243 if (firstPt && active) {
4244 #if DEBUG_PATH_CONSTRUCTION
4245 SkDebugf("%s close\n", __FUNCTION__);
4246 #endif
4247 simple.close();
4248 }
caryclark@google.come21cb182012-07-23 21:26:31 +00004249 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004250 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00004251 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004252 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004253 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00004254 break;
4255 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004256 int lesser = SkMin32(index, endIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004257 spanWinding = current->spanSign(index, endIndex);
4258 winding = current->windSum(lesser);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004259 bool inner = useInnerWinding(winding - spanWinding, winding);
4260 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004261 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
caryclark@google.com59823f72012-08-09 18:17:47 +00004262 " inner=%d result=%d\n",
caryclark@google.com2ddff932012-08-07 21:25:27 +00004263 __FUNCTION__, current->debugID(), current->t(lesser),
4264 spanWinding, winding, SkSign32(index - endIndex),
4265 useInnerWinding(winding - spanWinding, winding),
caryclark@google.com2ddff932012-08-07 21:25:27 +00004266 inner ? winding - spanWinding : winding);
4267 #endif
4268 if (inner) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004269 winding -= spanWinding;
4270 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004271 active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004272 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004273 } while (true);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004274}
4275
caryclark@google.com24bec792012-08-20 12:43:57 +00004276static void bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
4277 Segment* current;
4278 int start, end;
4279 while ((current = findUndone(contourList, start, end))) {
4280 const SkPoint* firstPt = NULL;
4281 SkPoint lastPt;
4282 do {
4283 SkASSERT(!current->done());
4284 int nextStart = start;
4285 int nextEnd = end;
4286 Segment* next = current->findNextXor(nextStart, nextEnd);
4287 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00004288 if (firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
4289 lastPt = current->addCurveTo(start, end, simple, true);
4290 SkASSERT(*firstPt == lastPt);
4291 }
caryclark@google.com24bec792012-08-20 12:43:57 +00004292 break;
4293 }
4294 if (!firstPt) {
4295 firstPt = &current->addMoveTo(start, simple, true);
4296 }
4297 lastPt = current->addCurveTo(start, end, simple, true);
4298 current = next;
4299 start = nextStart;
4300 end = nextEnd;
4301 } while (*firstPt != lastPt);
4302 if (firstPt) {
4303 #if DEBUG_PATH_CONSTRUCTION
4304 SkDebugf("%s close\n", __FUNCTION__);
4305 #endif
4306 simple.close();
4307 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004308 }
caryclark@google.com24bec792012-08-20 12:43:57 +00004309}
4310
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004311static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
4312 int contourCount = contourList.count();
4313 for (int cTest = 0; cTest < contourCount; ++cTest) {
4314 Contour* contour = contourList[cTest];
4315 contour->fixOtherTIndex();
4316 }
4317}
4318
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004319static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004320 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004321 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004322 if (count == 0) {
4323 return;
4324 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004325 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004326 *list.append() = &contours[index];
4327 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004328 QSort<Contour>(list.begin(), list.end() - 1);
4329}
4330
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004331void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004332 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004333 simple.reset();
4334 simple.setFillType(SkPath::kEvenOdd_FillType);
4335
4336 // turn path into list of segments
4337 SkTArray<Contour> contours;
4338 // FIXME: add self-intersecting cubics' T values to segment
4339 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004340 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004341 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004342 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004343 Contour** currentPtr = contourList.begin();
4344 if (!currentPtr) {
4345 return;
4346 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004347 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004348 // find all intersections between segments
4349 do {
4350 Contour** nextPtr = currentPtr;
4351 Contour* current = *currentPtr++;
4352 Contour* next;
4353 do {
4354 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004355 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004356 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004357 // eat through coincident edges
caryclark@google.com235f56a2012-09-14 14:19:30 +00004358 coincidenceCheck(contourList);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004359 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004360 // construct closed contours
caryclark@google.com235f56a2012-09-14 14:19:30 +00004361 if (builder.xorMask() == kWinding_Mask) {
caryclark@google.com24bec792012-08-20 12:43:57 +00004362 bridgeWinding(contourList, simple);
4363 } else {
4364 bridgeXor(contourList, simple);
4365 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004366}
4367