blob: 2ce83d686c5600cdcb3c31ebc221aea6305a006c [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
caryclark@google.com47580692012-07-23 12:14:49 +000020// An Edge is a Segment generated from a Span
caryclark@google.com15fa1382012-05-07 20:49:36 +000021
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022// FIXME: remove once debugging is complete
caryclark@google.com47580692012-07-23 12:14:49 +000023#ifdef SK_DEBUG
24int gDebugMaxWindSum = SK_MaxS32;
25int gDebugMaxWindValue = SK_MaxS32;
26#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +000027
caryclark@google.com47580692012-07-23 12:14:49 +000028#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa0588f2012-04-26 21:01:06 +000029
caryclark@google.com6aea33f2012-10-09 14:11:58 +000030#if 0 // set to 1 for multiple thread -- no debugging
caryclark@google.com47580692012-07-23 12:14:49 +000031
32const bool gRunTestsInOneThread = false;
33
34#define DEBUG_ACTIVE_SPANS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000035#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000036#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000037#define DEBUG_ANGLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000038#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000039#define DEBUG_CROSS 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000040#define DEBUG_MARK_DONE 0
caryclark@google.com6aea33f2012-10-09 14:11:58 +000041#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000042#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000043#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000044#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000045
46#else
47
caryclark@google.com47580692012-07-23 12:14:49 +000048const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000049
caryclark@google.comafe56de2012-07-24 18:11:03 +000050#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000051#define DEBUG_ADD_INTERSECTING_TS 1
52#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000053#define DEBUG_ANGLE 1
54#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000055#define DEBUG_CROSS 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000056#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000057#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000058#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000059#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000060#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000061
62#endif
63
caryclark@google.com6aea33f2012-10-09 14:11:58 +000064#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000065
caryclark@google.comfa0588f2012-04-26 21:01:06 +000066#if DEBUG_DUMP
67static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000068// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000069static int gContourID;
70static int gSegmentID;
71#endif
72
caryclark@google.com8dcf1142012-07-02 20:27:02 +000073#ifndef DEBUG_TEST
74#define DEBUG_TEST 0
75#endif
76
caryclark@google.com32546db2012-08-31 20:55:07 +000077#define MAKE_CONST_LINE(line, pts) \
78 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
79#define MAKE_CONST_QUAD(quad, pts) \
80 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
81 {pts[2].fX, pts[2].fY}}
82#define MAKE_CONST_CUBIC(cubic, pts) \
83 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
84 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
85
caryclark@google.comfa0588f2012-04-26 21:01:06 +000086static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
87 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000088 MAKE_CONST_LINE(aLine, a);
89 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +000090 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
91}
92
93static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
94 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000095 MAKE_CONST_QUAD(aQuad, a);
96 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +000097 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +000098}
99
caryclark@google.com32546db2012-08-31 20:55:07 +0000100static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000101 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000102 MAKE_CONST_CUBIC(aCubic, a);
103 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000104 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
105}
106
107static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
108 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000109 MAKE_CONST_QUAD(aQuad, a);
110 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000111#define TRY_QUARTIC_SOLUTION 1
112#if TRY_QUARTIC_SOLUTION
113 intersect2(aQuad, bQuad, intersections);
114#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000115 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000116#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000117 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000118}
119
120static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
121 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000122 MAKE_CONST_CUBIC(aCubic, a);
123 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000124 intersect(aCubic, bCubic, intersections);
125 return intersections.fUsed;
126}
127
128static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
129 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000130 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000131 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
132}
133
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000134static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
135 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000136 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000137 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
138}
139
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000140static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
141 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000142 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000143 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
144}
145
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000146static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
147 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000148 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000149 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
150}
151
152static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
153 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000154 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000155 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
156}
157
158static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
159 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000160 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000161 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000162}
163
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000164static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
165 SkScalar , SkScalar , bool , Intersections& ) = {
166 NULL,
167 VLineIntersect,
168 VQuadIntersect,
169 VCubicIntersect
170};
171
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000172static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000173 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000174 double x, y;
175 xy_at_t(line, t, x, y);
176 out->fX = SkDoubleToScalar(x);
177 out->fY = SkDoubleToScalar(y);
178}
179
180static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000181 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000182 double x, y;
183 xy_at_t(quad, t, x, y);
184 out->fX = SkDoubleToScalar(x);
185 out->fY = SkDoubleToScalar(y);
186}
187
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000188static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
189 MAKE_CONST_QUAD(quad, a);
190 xy_at_t(quad, t, out->x, out->y);
191}
192
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000193static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000194 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000195 double x, y;
196 xy_at_t(cubic, t, x, y);
197 out->fX = SkDoubleToScalar(x);
198 out->fY = SkDoubleToScalar(y);
199}
200
201static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
202 NULL,
203 LineXYAtT,
204 QuadXYAtT,
205 CubicXYAtT
206};
207
208static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000209 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000210 double x;
211 xy_at_t(aLine, t, x, *(double*) 0);
212 return SkDoubleToScalar(x);
213}
214
215static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000216 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000217 double x;
218 xy_at_t(quad, t, x, *(double*) 0);
219 return SkDoubleToScalar(x);
220}
221
222static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000223 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000224 double x;
225 xy_at_t(cubic, t, x, *(double*) 0);
226 return SkDoubleToScalar(x);
227}
228
229static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
230 NULL,
231 LineXAtT,
232 QuadXAtT,
233 CubicXAtT
234};
235
236static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000237 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000238 double y;
239 xy_at_t(aLine, t, *(double*) 0, y);
240 return SkDoubleToScalar(y);
241}
242
243static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000244 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000245 double y;
246 xy_at_t(quad, t, *(double*) 0, y);
247 return SkDoubleToScalar(y);
248}
249
250static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000251 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000252 double y;
253 xy_at_t(cubic, t, *(double*) 0, y);
254 return SkDoubleToScalar(y);
255}
256
257static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
258 NULL,
259 LineYAtT,
260 QuadYAtT,
261 CubicYAtT
262};
263
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000264static SkScalar LineDXAtT(const SkPoint a[2], double ) {
265 return a[1].fX - a[0].fX;
266}
267
268static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000269 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000270 double x;
271 dxdy_at_t(quad, t, x, *(double*) 0);
272 return SkDoubleToScalar(x);
273}
274
275static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000276 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000277 double x;
278 dxdy_at_t(cubic, t, x, *(double*) 0);
279 return SkDoubleToScalar(x);
280}
281
282static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
283 NULL,
284 LineDXAtT,
285 QuadDXAtT,
286 CubicDXAtT
287};
288
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000289static void LineSubDivide(const SkPoint a[2], double startT, double endT,
290 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000291 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000292 _Line dst;
293 sub_divide(aLine, startT, endT, dst);
294 sub[0].fX = SkDoubleToScalar(dst[0].x);
295 sub[0].fY = SkDoubleToScalar(dst[0].y);
296 sub[1].fX = SkDoubleToScalar(dst[1].x);
297 sub[1].fY = SkDoubleToScalar(dst[1].y);
298}
299
300static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
301 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000302 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000303 Quadratic dst;
304 sub_divide(aQuad, startT, endT, dst);
305 sub[0].fX = SkDoubleToScalar(dst[0].x);
306 sub[0].fY = SkDoubleToScalar(dst[0].y);
307 sub[1].fX = SkDoubleToScalar(dst[1].x);
308 sub[1].fY = SkDoubleToScalar(dst[1].y);
309 sub[2].fX = SkDoubleToScalar(dst[2].x);
310 sub[2].fY = SkDoubleToScalar(dst[2].y);
311}
312
313static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
314 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000315 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000316 Cubic dst;
317 sub_divide(aCubic, startT, endT, dst);
318 sub[0].fX = SkDoubleToScalar(dst[0].x);
319 sub[0].fY = SkDoubleToScalar(dst[0].y);
320 sub[1].fX = SkDoubleToScalar(dst[1].x);
321 sub[1].fY = SkDoubleToScalar(dst[1].y);
322 sub[2].fX = SkDoubleToScalar(dst[2].x);
323 sub[2].fY = SkDoubleToScalar(dst[2].y);
324 sub[3].fX = SkDoubleToScalar(dst[3].x);
325 sub[3].fY = SkDoubleToScalar(dst[3].y);
326}
327
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000328static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
329 SkPoint []) = {
330 NULL,
331 LineSubDivide,
332 QuadSubDivide,
333 CubicSubDivide
334};
335
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000336static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000337 _Line sub) {
338 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000339 _Line dst;
340 sub_divide(aLine, startT, endT, dst);
341 sub[0] = dst[0];
342 sub[1] = dst[1];
343}
344
345static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000346 Quadratic sub) {
347 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000348 Quadratic dst;
349 sub_divide(aQuad, startT, endT, dst);
350 sub[0] = dst[0];
351 sub[1] = dst[1];
352 sub[2] = dst[2];
353}
354
355static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000356 Cubic sub) {
357 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000358 Cubic dst;
359 sub_divide(aCubic, startT, endT, dst);
360 sub[0] = dst[0];
361 sub[1] = dst[1];
362 sub[2] = dst[2];
363 sub[3] = dst[3];
364}
365
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000366#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000367static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
368 SkRect& bounds) {
369 SkPoint dst[3];
370 QuadSubDivide(a, startT, endT, dst);
371 bounds.fLeft = bounds.fRight = dst[0].fX;
372 bounds.fTop = bounds.fBottom = dst[0].fY;
373 for (int index = 1; index < 3; ++index) {
374 bounds.growToInclude(dst[index].fX, dst[index].fY);
375 }
376}
377
378static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
379 SkRect& bounds) {
380 SkPoint dst[4];
381 CubicSubDivide(a, startT, endT, dst);
382 bounds.fLeft = bounds.fRight = dst[0].fX;
383 bounds.fTop = bounds.fBottom = dst[0].fY;
384 for (int index = 1; index < 4; ++index) {
385 bounds.growToInclude(dst[index].fX, dst[index].fY);
386 }
387}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000388#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000389
caryclark@google.com15fa1382012-05-07 20:49:36 +0000390static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000391 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000392 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000393 Quadratic dst;
394 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000395 if (order == 2) { // quad became line
396 for (int index = 0; index < order; ++index) {
397 SkPoint* pt = reducePts.append();
398 pt->fX = SkDoubleToScalar(dst[index].x);
399 pt->fY = SkDoubleToScalar(dst[index].y);
400 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000401 }
402 return (SkPath::Verb) (order - 1);
403}
404
405static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
406 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000407 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000408 Cubic dst;
409 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000410 if (order == 2 || order == 3) { // cubic became line or quad
411 for (int index = 0; index < order; ++index) {
412 SkPoint* pt = reducePts.append();
413 pt->fX = SkDoubleToScalar(dst[index].x);
414 pt->fY = SkDoubleToScalar(dst[index].y);
415 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000416 }
417 return (SkPath::Verb) (order - 1);
418}
419
caryclark@google.com15fa1382012-05-07 20:49:36 +0000420static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000421 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000422 return isLinear(aQuad, 0, 2);
423}
424
425static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000426 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000427 return isLinear(aCubic, 0, 3);
428}
429
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000430static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000431 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000432 double x[2];
433 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000434 xy_at_t(aLine, endT, x[1], *(double*) 0);
435 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000436}
437
438static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000439 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000440 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000441}
442
443static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000444 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000445 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000446}
447
448static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
449 NULL,
450 LineLeftMost,
451 QuadLeftMost,
452 CubicLeftMost
453};
454
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000455#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000456static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
457 Intersections& intersections) {
458 MAKE_CONST_QUAD(aQuad, a);
459 MAKE_CONST_LINE(bLine, b);
460 return intersectRay(aQuad, bLine, intersections);
461}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000462#endif
463
464static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
465 Intersections& intersections) {
466 MAKE_CONST_QUAD(aQuad, a);
467 return intersectRay(aQuad, bLine, intersections);
468}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000469
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000470class Segment;
471
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000472struct Span {
473 Segment* fOther;
474 mutable SkPoint fPt; // lazily computed as needed
475 double fT;
476 double fOtherT; // value at fOther[fOtherIndex].fT
477 int fOtherIndex; // can't be used during intersection
478 int fWindSum; // accumulated from contours surrounding this one
479 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
480 int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
481 bool fDone; // if set, this span to next higher T has been processed
482};
483
caryclark@google.com15fa1382012-05-07 20:49:36 +0000484// sorting angles
485// given angles of {dx dy ddx ddy dddx dddy} sort them
486class Angle {
487public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000488 // FIXME: this is bogus for quads and cubics
489 // if the quads and cubics' line from end pt to ctrl pt are coincident,
490 // there's no obvious way to determine the curve ordering from the
491 // derivatives alone. In particular, if one quadratic's coincident tangent
492 // is longer than the other curve, the final control point can place the
493 // longer curve on either side of the shorter one.
494 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
495 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000496
caryclark@google.com32546db2012-08-31 20:55:07 +0000497 // start here
498 /*(
499 for quads and cubics, set up a parameterized line (e.g. LineParameters )
500 for points [0] to [1]. See if point [2] is on that line, or on one side
501 or the other. If it both quads' end points are on the same side, choose
502 the shorter tangent. If the tangents are equal, choose the better second
503 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000504
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000505 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000506 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000507 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000508 double y = dy();
509 double ry = rh.dy();
510 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
511 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000512 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000513 double x = dx();
514 double rx = rh.dx();
515 if (y == 0 && ry == 0 && x * rx < 0) {
516 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000517 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000518 double x_ry = x * ry;
519 double rx_y = rx * y;
520 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000521 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000522 return cmp < 0;
523 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000524 if (approximately_zero(x_ry) && approximately_zero(rx_y)
525 && !approximately_zero_squared(cmp)) {
526 return cmp < 0;
527 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000528 // see if either curve can be lengthened and try the tangent compare again
529 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
530 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
531 Angle longer = *this;
532 Angle rhLonger = rh;
533 if (longer.lengthen() | rhLonger.lengthen()) {
534 return longer < rhLonger;
535 }
536 }
caryclark@google.com32546db2012-08-31 20:55:07 +0000537 // at this point, the initial tangent line is coincident
caryclark@google.com32546db2012-08-31 20:55:07 +0000538 if (fSide * rh.fSide <= 0) {
caryclark@google.comd1688742012-09-18 20:08:37 +0000539 // FIXME: running demo will trigger this assertion
540 // (don't know if commenting out will trigger further assertion or not)
541 // commenting it out allows demo to run in release, though
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000542 // SkASSERT(fSide != rh.fSide);
caryclark@google.com32546db2012-08-31 20:55:07 +0000543 return fSide < rh.fSide;
544 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000545 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
546 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000547 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000548 // end of the shorter tangent to midway between the end points
549 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000550 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
551 double len = fTangent1.normalSquared();
552 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000553 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000554 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000555 int roots, rroots;
556 bool flip = false;
557 do {
558 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
559 double midX = (q[0].x + q[2].x) / 2;
560 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000561 ray[0] = q[1];
562 ray[1].x = midX;
563 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000564 SkASSERT(ray[0] != ray[1]);
565 roots = QuadRayIntersect(fPts, ray, i);
566 rroots = QuadRayIntersect(rh.fPts, ray, ri);
567 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.com235f56a2012-09-14 14:19:30 +0000568 SkASSERT(roots > 0);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000569 SkASSERT(rroots > 0);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000570 _Point loc;
571 double best = SK_ScalarInfinity;
572 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000573 int index;
574 for (index = 0; index < roots; ++index) {
575 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000576 dx = loc.x - ray[0].x;
577 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000578 dist = dx * dx + dy * dy;
579 if (best > dist) {
580 best = dist;
581 }
582 }
583 for (index = 0; index < rroots; ++index) {
584 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000585 dx = loc.x - ray[0].x;
586 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000587 dist = dx * dx + dy * dy;
588 if (best > dist) {
589 return fSide < 0;
590 }
591 }
592 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000593 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000594
caryclark@google.com47580692012-07-23 12:14:49 +0000595 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000596 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000597 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000598
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000599 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000600 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000601 }
602
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000603 int end() const {
604 return fEnd;
605 }
606
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000607 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000608 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000609 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000610
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000611 bool lengthen() {
612 int newEnd = fEnd;
613 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
614 fEnd = newEnd;
615 setSpans();
616 return true;
617 }
618 return false;
619 }
620
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000621 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000622 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000623 fSegment = segment;
624 fStart = start;
625 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000626 fPts = orig;
627 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000628 fSpans = &spans;
629 setSpans();
630 }
631
632 void setSpans() {
633 double startT = (*fSpans)[fStart].fT;
634 double endT = (*fSpans)[fEnd].fT;
635 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000636 case SkPath::kLine_Verb:
637 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000638 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000639 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
640 fTangent1.lineEndPoints(l);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000641 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000642 break;
643 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000644 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000645 fTangent1.quadEndPoints(fQ, 0, 1);
646 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000647 break;
648 case SkPath::kCubic_Verb:
649 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000650 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000651 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000652 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000653 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000654 default:
655 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000656 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000657 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000658
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000659 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000660 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000661 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000662
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000663 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000664 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000665 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000666
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000667 int start() const {
668 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000669 }
670
caryclark@google.comc899ad92012-08-23 15:24:42 +0000671#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000672 const SkPoint* pts() const {
673 return fPts;
674 }
675
676 const SkTDArray<Span>* spans() const {
677 return fSpans;
678 }
679
680 SkPath::Verb verb() const {
681 return fVerb;
682 }
683
caryclark@google.comc899ad92012-08-23 15:24:42 +0000684 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000685 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000686 }
687#endif
688
caryclark@google.com15fa1382012-05-07 20:49:36 +0000689private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000690 const SkPoint* fPts;
691 Quadratic fQ;
692 SkPath::Verb fVerb;
693 double fSide;
694 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000695 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000696 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000697 int fStart;
698 int fEnd;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000699};
700
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000701static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
702 int angleCount = angles.count();
703 int angleIndex;
704 angleList.setReserve(angleCount);
705 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
706 *angleList.append() = &angles[angleIndex];
707 }
708 QSort<Angle>(angleList.begin(), angleList.end() - 1);
709}
710
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000711// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000712struct Bounds : public SkRect {
713 static bool Intersects(const Bounds& a, const Bounds& b) {
714 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
715 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
716 }
717
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000718 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
719 if (left < fLeft) {
720 fLeft = left;
721 }
722 if (top < fTop) {
723 fTop = top;
724 }
725 if (right > fRight) {
726 fRight = right;
727 }
728 if (bottom > fBottom) {
729 fBottom = bottom;
730 }
731 }
732
733 void add(const Bounds& toAdd) {
734 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
735 }
736
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000737 bool isEmpty() {
738 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000739 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000740 || isnan(fLeft) || isnan(fRight)
741 || isnan(fTop) || isnan(fBottom);
742 }
743
744 void setCubicBounds(const SkPoint a[4]) {
745 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000746 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000747 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000748 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
749 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000750 }
751
752 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000753 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000754 _Rect dRect;
755 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000756 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
757 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000758 }
759};
760
caryclark@google.com2ddff932012-08-07 21:25:27 +0000761static bool useInnerWinding(int outerWinding, int innerWinding) {
762 SkASSERT(outerWinding != innerWinding);
763 int absOut = abs(outerWinding);
764 int absIn = abs(innerWinding);
765 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
766 if (outerWinding * innerWinding < 0) {
767#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000768 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000769 outerWinding, innerWinding, result ? "true" : "false");
770#endif
771 }
772 return result;
773}
774
caryclark@google.com235f56a2012-09-14 14:19:30 +0000775static const bool opLookup[][2][2] = {
776 // ==0 !=0
777 // b a b a
778 {{true , false}, {false, true }}, // a - b
779 {{false, false}, {true , true }}, // a & b
780 {{true , true }, {false, false}}, // a | b
781 {{true , true }, {true , true }}, // a ^ b
782};
783
784static bool activeOp(bool angleIsOp, int otherNonZero, ShapeOp op) {
785 return opLookup[op][otherNonZero][angleIsOp];
786}
787
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000788class Segment {
789public:
790 Segment() {
791#if DEBUG_DUMP
792 fID = ++gSegmentID;
793#endif
794 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000795
caryclark@google.com9764cc62012-07-12 19:29:45 +0000796 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
797 if (activeAngleInner(index, done, angles)) {
798 return true;
799 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000800 double referenceT = fTs[index].fT;
801 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000802 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000803 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000804 return true;
805 }
806 }
807 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000808 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000809 return true;
810 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000811 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000812 return false;
813 }
814
caryclark@google.com9764cc62012-07-12 19:29:45 +0000815 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000816 Span* span = &fTs[index];
817 Segment* other = span->fOther;
818 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +0000819 return other->activeAngleInner(oIndex, done, angles);
820 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000821
caryclark@google.com9764cc62012-07-12 19:29:45 +0000822 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
823 int next = nextSpan(index, 1);
824 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000825 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000826 if (upSpan.fWindValue) {
827 addAngle(angles, index, next);
828 if (upSpan.fDone) {
829 done++;
830 } else if (upSpan.fWindSum != SK_MinS32) {
831 return true;
832 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000833 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000834 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000835 int prev = nextSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000836 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +0000837 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000838 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000839 if (downSpan.fWindValue) {
840 addAngle(angles, index, prev);
841 if (downSpan.fDone) {
842 done++;
843 } else if (downSpan.fWindSum != SK_MinS32) {
844 return true;
845 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000846 }
847 }
848 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000849 }
850
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000851 SkScalar activeTop() const {
852 SkASSERT(!done());
853 int count = fTs.count();
854 SkScalar result = SK_ScalarMax;
855 bool lastDone = true;
856 for (int index = 0; index < count; ++index) {
857 bool done = fTs[index].fDone;
858 if (!done || !lastDone) {
859 SkScalar y = yAtT(index);
860 if (result > y) {
861 result = y;
862 }
863 }
864 lastDone = done;
865 }
866 SkASSERT(result < SK_ScalarMax);
867 return result;
868 }
869
870 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000871 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000872 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000873#if DEBUG_ANGLE
874 if (angles.count() > 1) {
875 SkPoint angle0Pt, newPt;
876 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
877 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
878 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
879 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
880 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
881 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000882#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000883 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000884 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000885
caryclark@google.com2ddff932012-08-07 21:25:27 +0000886 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +0000887 double oEnd) {
888 int tIndex = -1;
889 int tCount = fTs.count();
890 int oIndex = -1;
891 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +0000892 do {
893 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000894 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +0000895 int tIndexStart = tIndex;
896 do {
897 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000898 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +0000899 int oIndexStart = oIndex;
900 double nextT;
901 do {
902 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000903 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000904 double oNextT;
905 do {
906 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000907 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000908 // at this point, spans before and after are at:
909 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
910 // if tIndexStart == 0, no prior span
911 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +0000912
caryclark@google.comcc905052012-07-25 20:59:42 +0000913 // advance the span with zero winding
914 // if the following span exists (not past the end, non-zero winding)
915 // connect the two edges
916 if (!fTs[tIndexStart].fWindValue) {
917 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
918 #if DEBUG_CONCIDENT
919 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
920 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000921 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
922 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +0000923 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000924 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000925 }
926 if (nextT < 1 && fTs[tIndex].fWindValue) {
927 #if DEBUG_CONCIDENT
928 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
929 __FUNCTION__, fID, other.fID, tIndex,
930 fTs[tIndex].fT, xyAtT(tIndex).fX,
931 xyAtT(tIndex).fY);
932 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000933 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000934 }
935 } else {
936 SkASSERT(!other.fTs[oIndexStart].fWindValue);
937 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
938 #if DEBUG_CONCIDENT
939 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
940 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000941 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
942 other.xyAtT(oIndexStart).fY);
943 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +0000944 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +0000945 }
946 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
947 #if DEBUG_CONCIDENT
948 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
949 __FUNCTION__, fID, other.fID, oIndex,
950 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
951 other.xyAtT(oIndex).fY);
952 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
953 #endif
954 }
955 }
956 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000957
caryclark@google.comcc905052012-07-25 20:59:42 +0000958 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
959 double oEnd) {
960 // walk this to outsideTs[0]
961 // walk other to outsideTs[1]
962 // if either is > 0, add a pointer to the other, copying adjacent winding
963 int tIndex = -1;
964 int oIndex = -1;
965 double tStart = outsideTs[0];
966 double oStart = outsideTs[1];
967 do {
968 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000969 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +0000970 do {
971 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000972 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +0000973 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +0000974 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000975 }
976 tStart = fTs[tIndex].fT;
977 oStart = other.fTs[oIndex].fT;
978 do {
979 double nextT;
980 do {
981 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000982 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000983 tStart = nextT;
984 do {
985 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000986 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000987 oStart = nextT;
988 if (tStart == 1 && oStart == 1) {
989 break;
990 }
caryclark@google.com2ddff932012-08-07 21:25:27 +0000991 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000992 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000993 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000994
caryclark@google.com235f56a2012-09-14 14:19:30 +0000995 void addCubic(const SkPoint pts[4], bool operand) {
996 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000997 fBounds.setCubicBounds(pts);
998 }
999
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001000 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001001 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001002 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001003 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001004 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001005 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001006 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001007 SkDebugf("path.%sTo(%1.9g,%1.9g",
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001008 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
1009 if (fVerb > 1) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001010 SkDebugf(", %1.9g,%1.9g", edge[2].fX, edge[2].fY);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001011 }
1012 if (fVerb > 2) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001013 SkDebugf(", %1.9g,%1.9g", edge[3].fX, edge[3].fY);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001014 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001015 SkDebugf(");\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001016 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001017 switch (fVerb) {
1018 case SkPath::kLine_Verb:
1019 path.lineTo(edge[1].fX, edge[1].fY);
1020 break;
1021 case SkPath::kQuad_Verb:
1022 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
1023 break;
1024 case SkPath::kCubic_Verb:
1025 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
1026 edge[3].fX, edge[3].fY);
1027 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001028 default:
1029 SkASSERT(0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001030 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001031 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001032 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001033 }
1034
caryclark@google.com235f56a2012-09-14 14:19:30 +00001035 void addLine(const SkPoint pts[2], bool operand) {
1036 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001037 fBounds.set(pts, 2);
1038 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001039
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001040 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
1041 const SkPoint& pt = xyAtT(tIndex);
1042 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001043 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001044 SkDebugf("path.moveTo(%1.9g, %1.9g);\n", pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001045 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001046 path.moveTo(pt.fX, pt.fY);
1047 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001048 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001049 }
1050
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001051 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001052 void addOtherT(int index, double otherT, int otherIndex) {
1053 Span& span = fTs[index];
1054 span.fOtherT = otherT;
1055 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001056 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001057
caryclark@google.com235f56a2012-09-14 14:19:30 +00001058 void addQuad(const SkPoint pts[3], bool operand) {
1059 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001060 fBounds.setQuadBounds(pts);
1061 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001062
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001063 // Defer all coincident edge processing until
1064 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001065
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001066// no need to be tricky; insert in normal T order
1067// resolve overlapping ts when considering coincidence later
1068
1069 // add non-coincident intersection. Resulting edges are sorted in T.
1070 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001071 // FIXME: in the pathological case where there is a ton of intercepts,
1072 // binary search?
1073 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001074 size_t tCount = fTs.count();
caryclark@google.comc899ad92012-08-23 15:24:42 +00001075 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001076 if (approximately_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001077 newT = 0;
1078 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001079 if (approximately_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001080 newT = 1;
1081 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001082 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001083 // OPTIMIZATION: if there are three or more identical Ts, then
1084 // the fourth and following could be further insertion-sorted so
1085 // that all the edges are clockwise or counterclockwise.
1086 // This could later limit segment tests to the two adjacent
1087 // neighbors, although it doesn't help with determining which
1088 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001089 if (newT < fTs[index].fT) {
1090 insertedAt = index;
1091 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001092 }
1093 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001094 Span* span;
1095 if (insertedAt >= 0) {
1096 span = fTs.insert(insertedAt);
1097 } else {
1098 insertedAt = tCount;
1099 span = fTs.append();
1100 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001101 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001102 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001103 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001104 span->fWindSum = SK_MinS32;
1105 span->fWindValue = 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001106 span->fWindValueOpp = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001107 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001108 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001109 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001110 return insertedAt;
1111 }
1112
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001113 // set spans from start to end to decrement by one
1114 // note this walks other backwards
1115 // FIMXE: there's probably an edge case that can be constructed where
1116 // two span in one segment are separated by float epsilon on one span but
1117 // not the other, if one segment is very small. For this
1118 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001119 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001120 // sorted? It feels better in such a case to match the span's other span
1121 // pointer since both coincident segments must contain the same spans.
1122 void addTCancel(double startT, double endT, Segment& other,
1123 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001124 SkASSERT(!approximately_negative(endT - startT));
1125 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001126 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001127 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001128 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001129 ++index;
1130 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001131 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001132 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001133 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001134 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001135 Span* test = &fTs[index];
1136 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001137 SkTDArray<double> outsideTs;
1138 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001139 do {
1140 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001141 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001142 double testT = test->fT;
1143 double oTestT = oTest->fT;
1144 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001145 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001146 if (decrement) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001147 if (binary) {
1148 --(span->fWindValueOpp);
1149 } else {
1150 decrementSpan(span);
1151 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001152 } else if (track && span->fT < 1 && oTestT < 1) {
1153 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001154 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001155 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001156 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001157 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001158 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1159 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1160 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001161 while (approximately_negative(otherTMatchStart - oSpan->fT)
1162 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001163 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001164 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001165 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001166 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001167 other.decrementSpan(oSpan);
1168 } else if (track && oSpan->fT < 1 && testT < 1) {
1169 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001170 }
1171 if (!oIndex) {
1172 break;
1173 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001174 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001175 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001176 test = span;
1177 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001178 } while (!approximately_negative(endT - test->fT));
1179 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001180 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001181 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001182 double tStart = outsideTs[0];
1183 double oStart = outsideTs[1];
1184 addCancelOutsides(tStart, oStart, other, oEndT);
1185 int count = outsideTs.count();
1186 if (count > 2) {
1187 double tStart = outsideTs[count - 2];
1188 double oStart = outsideTs[count - 1];
1189 addCancelOutsides(tStart, oStart, other, oEndT);
1190 }
caryclark@google.com18063442012-07-25 12:05:18 +00001191 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001192 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001193 double tStart = oOutsideTs[0];
1194 double oStart = oOutsideTs[1];
1195 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001196 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001197 }
1198
1199 // set spans from start to end to increment the greater by one and decrement
1200 // the lesser
caryclark@google.com235f56a2012-09-14 14:19:30 +00001201 void addTCoincident(const bool isXor, double startT, double endT,
1202 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001203 SkASSERT(!approximately_negative(endT - startT));
1204 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001205 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001206 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001207 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001208 ++index;
1209 }
1210 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001211 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001212 ++oIndex;
1213 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001214 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001215 Span* test = &fTs[index];
1216 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001217 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001218 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001219 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001220 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001221 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001222 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001223 bool decrementThis = (test->fWindValue < oTest->fWindValue) & !isXor;
1224 bool decrementOther = (test->fWindValue >= oTest->fWindValue) & !isXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001225 Span* end = test;
1226 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001227 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001228 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001229 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001230 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001231 if (transfer) {
1232 if (decrementOther) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001233 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001234 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001235 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001236 if (binary) {
1237 ++(end->fWindValueOpp);
1238 } else {
1239 ++(end->fWindValue);
1240 }
caryclark@google.com18063442012-07-25 12:05:18 +00001241 } else if (decrementSpan(end)) {
1242 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001243 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001244 } else if (oTest->fWindValue) {
1245 SkASSERT(!decrementOther);
1246 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1247 TrackOutside(xOutsideTs, end->fT, oStartT);
1248 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001249 }
1250 end = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001251 } while (approximately_negative(end->fT - test->fT));
caryclark@google.com59823f72012-08-09 18:17:47 +00001252 // because of the order in which coincidences are resolved, this and other
1253 // may not have the same intermediate points. Compute the corresponding
1254 // intermediate T values (using this as the master, other as the follower)
1255 // and walk other conditionally -- hoping that it catches up in the end
1256 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001257 Span* oEnd = oTest;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001258 while (!approximately_negative(oEndT - oEnd->fT) && approximately_negative(oEnd->fT - otherTMatch)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00001259 if (transfer) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001260 if (decrementThis) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001261 #ifdef SK_DEBUG
1262 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1263 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001264 if (binary) {
1265 ++(oEnd->fWindValueOpp);
1266 } else {
1267 ++(oEnd->fWindValue);
1268 }
caryclark@google.com18063442012-07-25 12:05:18 +00001269 } else if (other.decrementSpan(oEnd)) {
1270 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001271 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001272 } else if (test->fWindValue) {
1273 SkASSERT(!decrementOther);
1274 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1275 SkASSERT(0); // track for later?
1276 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001277 }
1278 oEnd = &other.fTs[++oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001279 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001280 test = end;
1281 oTest = oEnd;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001282 } while (!approximately_negative(endT - test->fT));
1283 SkASSERT(approximately_negative(oTest->fT - oEndT));
1284 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001285 if (!done()) {
1286 if (outsideTs.count()) {
1287 addCoinOutsides(outsideTs, other, oEndT);
1288 }
1289 if (xOutsideTs.count()) {
1290 addCoinOutsides(xOutsideTs, other, oEndT);
1291 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001292 }
1293 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001294 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001295 }
1296 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001297
caryclark@google.comcc905052012-07-25 20:59:42 +00001298 // FIXME: this doesn't prevent the same span from being added twice
1299 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001300 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001301 int tCount = fTs.count();
1302 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1303 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001304 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001305 break;
1306 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001307 if (approximately_negative(span.fT - t) && span.fOther == &other
1308 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001309#if DEBUG_ADD_T_PAIR
1310 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1311 __FUNCTION__, fID, t, other.fID, otherT);
1312#endif
1313 return;
1314 }
1315 }
caryclark@google.com47580692012-07-23 12:14:49 +00001316#if DEBUG_ADD_T_PAIR
1317 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1318 __FUNCTION__, fID, t, other.fID, otherT);
1319#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001320 int insertedAt = addT(t, &other);
1321 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001322 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001323 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001324 matchWindingValue(insertedAt, t, borrowWind);
1325 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001326 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001327
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001328 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001329 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001330 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1331 addAngle(angles, end, start);
1332 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001333 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001334 int step = SkSign32(end - start);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001335 int tIndex = nextSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001336 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001337 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001338 }
1339 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001340
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001341 const Bounds& bounds() const {
1342 return fBounds;
1343 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001344
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001345 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001346 double referenceT = fTs[index].fT;
1347 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001348 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001349 buildAnglesInner(lesser, angles);
1350 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001351 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001352 buildAnglesInner(index, angles);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001353 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001354 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001355
1356 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1357 Span* span = &fTs[index];
1358 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001359 // if there is only one live crossing, and no coincidence, continue
1360 // in the same direction
1361 // if there is coincidence, the only choice may be to reverse direction
1362 // find edge on either side of intersection
1363 int oIndex = span->fOtherIndex;
1364 // if done == -1, prior span has already been processed
1365 int step = 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001366 int next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001367 if (next < 0) {
1368 step = -step;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001369 next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001370 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001371 // add candidate into and away from junction
1372 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001373 }
1374
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001375 // figure out if the segment's ascending T goes clockwise or not
1376 // not enough context to write this as shown
1377 // instead, add all segments meeting at the top
1378 // sort them using buildAngleList
1379 // find the first in the sort
1380 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001381 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001382 SkASSERT(0); // incomplete
1383 return false;
1384 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001385
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001386 // FIXME may not need this at all
1387 // FIXME once I figure out the logic, merge this and too close to call
1388 // NOTE not sure if tiny triangles can ever form at the edge, so until we
1389 // see one, only worry about triangles that happen away from 0 and 1
1390 void collapseTriangles(bool isXor) {
1391 if (fTs.count() < 3) { // require t=0, x, 1 at minimum
1392 return;
1393 }
1394 int lastIndex = 1;
1395 double lastT;
1396 while (approximately_less_than_zero((lastT = fTs[lastIndex].fT))) {
1397 ++lastIndex;
1398 }
1399 if (approximately_greater_than_one(lastT)) {
1400 return;
1401 }
1402 int matchIndex = lastIndex;
1403 do {
1404 Span& match = fTs[++matchIndex];
1405 double matchT = match.fT;
1406 if (approximately_greater_than_one(matchT)) {
1407 return;
1408 }
1409 if (matchT == lastT) {
1410 goto nextSpan;
1411 }
1412 if (approximately_negative(matchT - lastT)) {
1413 Span& last = fTs[lastIndex];
1414 Segment* lOther = last.fOther;
1415 double lT = last.fOtherT;
1416 if (approximately_less_than_zero(lT) || approximately_greater_than_one(lT)) {
1417 goto nextSpan;
1418 }
1419 Segment* mOther = match.fOther;
1420 double mT = match.fOtherT;
1421 if (approximately_less_than_zero(mT) || approximately_greater_than_one(mT)) {
1422 goto nextSpan;
1423 }
1424 // add new point to connect adjacent spurs
1425 int count = lOther->fTs.count();
1426 for (int index = 0; index < count; ++index) {
1427 Span& span = lOther->fTs[index];
1428 if (span.fOther == mOther && approximately_zero(span.fOtherT - mT)) {
1429 goto nextSpan;
1430 }
1431 }
1432 mOther->addTPair(mT, *lOther, lT, false);
1433 // FIXME ? this could go on to detect that spans on mOther, lOther are now coincident
1434 }
1435 nextSpan:
1436 lastIndex = matchIndex;
1437 lastT = matchT;
1438 } while (true);
1439 }
1440
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001441 int computeSum(int startIndex, int endIndex) {
1442 SkTDArray<Angle> angles;
1443 addTwoAngles(startIndex, endIndex, angles);
1444 buildAngles(endIndex, angles);
caryclark@google.comd1688742012-09-18 20:08:37 +00001445 // OPTIMIZATION: check all angles to see if any have computed wind sum
1446 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001447 SkTDArray<Angle*> sorted;
1448 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001449#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00001450 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001451#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001452 int angleCount = angles.count();
1453 const Angle* angle;
1454 const Segment* base;
1455 int winding;
1456 int firstIndex = 0;
1457 do {
1458 angle = sorted[firstIndex];
1459 base = angle->segment();
1460 winding = base->windSum(angle);
1461 if (winding != SK_MinS32) {
1462 break;
1463 }
1464 if (++firstIndex == angleCount) {
1465 return SK_MinS32;
1466 }
1467 } while (true);
1468 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001469 int spanWinding = base->spanSign(angle);
1470 bool inner = useInnerWinding(winding + spanWinding, winding);
1471 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001472 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001473 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001474 inner ? winding + spanWinding : winding);
1475 #endif
1476 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001477 winding += spanWinding;
1478 }
1479 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001480 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001481 #endif
1482 int nextIndex = firstIndex + 1;
1483 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001484 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001485 do {
1486 if (nextIndex == angleCount) {
1487 nextIndex = 0;
1488 }
1489 angle = sorted[nextIndex];
1490 Segment* segment = angle->segment();
1491 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001492 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001493 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001494 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001495 maxWinding = winding;
1496 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001497 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001498 }
1499 } while (++nextIndex != lastIndex);
1500 return windSum(SkMin32(startIndex, endIndex));
1501 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001502
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001503 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001504 int bestT = -1;
1505 SkScalar top = bounds().fTop;
1506 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001507 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001508 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001509 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001510 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001511 if (fTs[start].fWindValue == 0) {
1512 continue;
1513 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001514 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001515 double startT = fTs[start].fT;
1516 double endT = fTs[end].fT;
1517 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001518 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001519 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001520 // FIXME: always use original and limit results to T values within
1521 // start t and end t.
1522 // OPTIMIZE: use specialty function that intersects ray with curve,
1523 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001524 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1525 false, intersections);
1526 if (pts == 0) {
1527 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001528 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001529 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1530 // if the intersection is edge on, wait for another one
1531 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001532 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001533 for (int index = 0; index < pts; ++index) {
1534 SkPoint pt;
1535 double foundT = intersections.fT[0][index];
1536 double testT = startT + (endT - startT) * foundT;
1537 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1538 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001539 if (fVerb > SkPath::kLine_Verb
1540 && !approximately_less_than_zero(foundT)
1541 && !approximately_greater_than_one(foundT)) {
1542 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1543 if (approximately_zero(dx)) {
1544 continue;
1545 }
1546 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001547 bestY = pt.fY;
1548 bestT = foundT < 1 ? start : end;
1549 hitT = testT;
1550 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001551 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001552 } while (fTs[end].fT != 1);
1553 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001554 }
caryclark@google.com18063442012-07-25 12:05:18 +00001555
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001556 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1557 // if a segment is connected to this one, consider it crossing
1558 int tIndex;
1559 if (fPts[0].fX == basePt.fX) {
1560 tIndex = 0;
1561 do {
1562 const Span& sSpan = fTs[tIndex];
1563 const Segment* sOther = sSpan.fOther;
1564 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1565 continue;
1566 }
1567 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1568 : sOther->fBounds.fRight > basePt.fX) {
1569 return true;
1570 }
1571 } while (fTs[++tIndex].fT == 0);
1572 }
1573 if (fPts[fVerb].fX == basePt.fX) {
1574 tIndex = fTs.count() - 1;
1575 do {
1576 const Span& eSpan = fTs[tIndex];
1577 const Segment* eOther = eSpan.fOther;
1578 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1579 continue;
1580 }
1581 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1582 : eOther->fBounds.fRight > basePt.fX) {
1583 return true;
1584 }
1585 } while (fTs[--tIndex].fT == 1);
1586 }
1587 return false;
1588 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001589
caryclark@google.com18063442012-07-25 12:05:18 +00001590 bool decrementSpan(Span* span) {
1591 SkASSERT(span->fWindValue > 0);
1592 if (--(span->fWindValue) == 0) {
1593 span->fDone = true;
1594 ++fDoneSpans;
1595 return true;
1596 }
1597 return false;
1598 }
1599
caryclark@google.com15fa1382012-05-07 20:49:36 +00001600 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001601 SkASSERT(fDoneSpans <= fTs.count());
1602 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001603 }
1604
caryclark@google.com47580692012-07-23 12:14:49 +00001605 bool done(const Angle& angle) const {
1606 int start = angle.start();
1607 int end = angle.end();
1608 const Span& mSpan = fTs[SkMin32(start, end)];
1609 return mSpan.fDone;
1610 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00001611
caryclark@google.com235f56a2012-09-14 14:19:30 +00001612 Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
1613 int& nextStart, int& nextEnd, int& winding, int& spanWinding, ShapeOp op,
1614 const int aXorMask, const int bXorMask) {
1615 const int startIndex = nextStart;
1616 const int endIndex = nextEnd;
1617 int outerWinding = winding;
1618 int innerWinding = winding + spanWinding;
1619 #if DEBUG_WINDING
1620 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1621 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
1622 #endif
1623 if (useInnerWinding(outerWinding, innerWinding)) {
1624 outerWinding = innerWinding;
1625 }
1626 SkASSERT(startIndex != endIndex);
1627 int count = fTs.count();
1628 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1629 : startIndex > 0);
1630 int step = SkSign32(endIndex - startIndex);
1631 int end = nextSpan(startIndex, step);
1632 SkASSERT(end >= 0);
1633 Span* endSpan = &fTs[end];
1634 Segment* other;
1635 if (isSimple(end)) {
1636 // mark the smaller of startIndex, endIndex done, and all adjacent
1637 // spans with the same T value (but not 'other' spans)
1638 #if DEBUG_WINDING
1639 SkDebugf("%s simple\n", __FUNCTION__);
1640 #endif
1641 markDone(SkMin32(startIndex, endIndex), outerWinding);
1642 other = endSpan->fOther;
1643 nextStart = endSpan->fOtherIndex;
1644 double startT = other->fTs[nextStart].fT;
1645 nextEnd = nextStart;
1646 do {
1647 nextEnd += step;
1648 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
1649 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1650 return other;
1651 }
1652 // more than one viable candidate -- measure angles to find best
1653 SkTDArray<Angle> angles;
1654 SkASSERT(startIndex - endIndex != 0);
1655 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1656 addTwoAngles(startIndex, end, angles);
1657 buildAngles(end, angles);
1658 SkTDArray<Angle*> sorted;
1659 sortAngles(angles, sorted);
1660 int angleCount = angles.count();
1661 int firstIndex = findStartingEdge(sorted, startIndex, end);
1662 SkASSERT(firstIndex >= 0);
1663 #if DEBUG_SORT
1664 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
1665 #endif
1666 SkASSERT(sorted[firstIndex]->segment() == this);
1667 #if DEBUG_WINDING
1668 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
1669 #endif
1670 int aSumWinding = winding;
1671 int bSumWinding = winding;
1672 bool angleIsOp = sorted[firstIndex]->segment()->operand();
1673 int angleSpan = spanSign(sorted[firstIndex]);
1674 if (angleIsOp) {
1675 bSumWinding -= angleSpan;
1676 } else {
1677 aSumWinding -= angleSpan;
1678 }
1679 int nextIndex = firstIndex + 1;
1680 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1681 const Angle* foundAngle = NULL;
1682 // FIXME: found done logic probably fails if there are more than 4
1683 // sorted angles. It should bias towards the first and last undone
1684 // edges -- but not sure that it won't choose a middle (incorrect)
1685 // edge if one is undone
1686 bool foundDone = false;
1687 bool foundDone2 = false;
1688 // iterate through the angle, and compute everyone's winding
1689 bool altFlipped = false;
1690 bool foundFlipped = false;
1691 int foundMax = SK_MinS32;
1692 int foundSum = SK_MinS32;
1693 Segment* nextSegment;
1694 int lastNonZeroSum = winding;
1695 do {
1696 if (nextIndex == angleCount) {
1697 nextIndex = 0;
1698 }
1699 const Angle* nextAngle = sorted[nextIndex];
1700 nextSegment = nextAngle->segment();
1701 angleIsOp = nextSegment->operand();
1702 int sumWinding = angleIsOp ? bSumWinding : aSumWinding;
1703 int maxWinding = sumWinding;
1704 if (sumWinding) {
1705 lastNonZeroSum = sumWinding;
1706 }
1707 sumWinding -= nextSegment->spanSign(nextAngle);
1708 int xorMask = nextSegment->operand() ? bXorMask : aXorMask;
1709 bool otherNonZero;
1710 if (angleIsOp) {
1711 bSumWinding = sumWinding;
1712 otherNonZero = aSumWinding & aXorMask;
1713 } else {
1714 aSumWinding = sumWinding;
1715 otherNonZero = bSumWinding & bXorMask;
1716 }
1717 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00001718 #if 0 && DEBUG_WINDING
caryclark@google.com235f56a2012-09-14 14:19:30 +00001719 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
1720 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
1721 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
1722 #endif
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00001723
caryclark@google.com235f56a2012-09-14 14:19:30 +00001724 if (!(sumWinding & xorMask) && activeOp(angleIsOp, otherNonZero, op)) {
1725 if (!active) {
1726 markDone(SkMin32(startIndex, endIndex), outerWinding);
1727 // FIXME: seems like a bug that this isn't calling userInnerWinding
1728 nextSegment->markWinding(SkMin32(nextAngle->start(),
1729 nextAngle->end()), maxWinding);
1730 #if DEBUG_WINDING
1731 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
1732 #endif
1733 return NULL;
1734 }
1735 if (!foundAngle || foundDone) {
1736 foundAngle = nextAngle;
1737 foundDone = nextSegment->done(*nextAngle);
1738 foundFlipped = altFlipped;
1739 foundMax = maxWinding;
1740 }
1741 continue;
1742 }
1743 if (!(maxWinding & xorMask) && (!foundAngle || foundDone2)
1744 && activeOp(angleIsOp, otherNonZero, op)) {
1745 #if DEBUG_WINDING
1746 if (foundAngle && foundDone2) {
1747 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
1748 }
1749 #endif
1750 foundAngle = nextAngle;
1751 foundDone2 = nextSegment->done(*nextAngle);
1752 foundFlipped = altFlipped;
1753 foundSum = sumWinding;
1754 }
1755 if (nextSegment->done()) {
1756 continue;
1757 }
1758 // if the winding is non-zero, nextAngle does not connect to
1759 // current chain. If we haven't done so already, mark the angle
1760 // as done, record the winding value, and mark connected unambiguous
1761 // segments as well.
1762 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
1763 if (useInnerWinding(maxWinding, sumWinding)) {
1764 maxWinding = sumWinding;
1765 }
1766 Span* last;
1767 if (foundAngle) {
1768 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
1769 } else {
1770 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
1771 }
1772 if (last) {
1773 *chase.append() = last;
1774 }
1775 }
1776 } while (++nextIndex != lastIndex);
1777 markDone(SkMin32(startIndex, endIndex), outerWinding);
1778 if (!foundAngle) {
1779 return NULL;
1780 }
1781 nextStart = foundAngle->start();
1782 nextEnd = foundAngle->end();
1783 nextSegment = foundAngle->segment();
1784 int flipped = foundFlipped ? -1 : 1;
1785 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
1786 SkMin32(nextStart, nextEnd));
1787 if (winding) {
1788 #if DEBUG_WINDING
1789 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
1790 if (foundSum == SK_MinS32) {
1791 SkDebugf("?");
1792 } else {
1793 SkDebugf("%d", foundSum);
1794 }
1795 SkDebugf(" foundMax=");
1796 if (foundMax == SK_MinS32) {
1797 SkDebugf("?");
1798 } else {
1799 SkDebugf("%d", foundMax);
1800 }
1801 SkDebugf("\n");
1802 #endif
1803 winding = foundSum;
1804 }
1805 #if DEBUG_WINDING
1806 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
1807 #endif
1808 return nextSegment;
1809 }
caryclark@google.com47580692012-07-23 12:14:49 +00001810
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001811 // so the span needs to contain the pairing info found here
1812 // this should include the winding computed for the edge, and
1813 // what edge it connects to, and whether it is discarded
1814 // (maybe discarded == abs(winding) > 1) ?
1815 // only need derivatives for duration of sorting, add a new struct
1816 // for pairings, remove extra spans that have zero length and
1817 // reference an unused other
1818 // for coincident, the last span on the other may be marked done
1819 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001820
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001821 // if loop is exhausted, contour may be closed.
1822 // FIXME: pass in close point so we can check for closure
1823
1824 // given a segment, and a sense of where 'inside' is, return the next
1825 // segment. If this segment has an intersection, or ends in multiple
1826 // segments, find the mate that continues the outside.
1827 // note that if there are multiples, but no coincidence, we can limit
1828 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00001829
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001830 // mark found segments as done
1831
caryclark@google.com15fa1382012-05-07 20:49:36 +00001832 // start is the index of the beginning T of this edge
1833 // it is guaranteed to have an end which describes a non-zero length (?)
1834 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00001835 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
1836 int& nextStart, int& nextEnd, int& winding, int& spanWinding) {
1837 const int startIndex = nextStart;
1838 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001839 int outerWinding = winding;
1840 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00001841 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001842 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1843 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00001844 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001845 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001846 outerWinding = innerWinding;
1847 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001848 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001849 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001850 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1851 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001852 int step = SkSign32(endIndex - startIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001853 int end = nextSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001854 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001855 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001856 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001857 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001858 // mark the smaller of startIndex, endIndex done, and all adjacent
1859 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001860 #if DEBUG_WINDING
1861 SkDebugf("%s simple\n", __FUNCTION__);
1862 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001863 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001864 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001865 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00001866 double startT = other->fTs[nextStart].fT;
1867 nextEnd = nextStart;
1868 do {
1869 nextEnd += step;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001870 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00001871 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001872 return other;
1873 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001874 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001875 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001876 SkASSERT(startIndex - endIndex != 0);
1877 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001878 addTwoAngles(startIndex, end, angles);
1879 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001880 SkTDArray<Angle*> sorted;
1881 sortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001882 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001883 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001884 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00001885 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001886 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001887 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001888 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001889 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001890 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00001891 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001892 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001893 int nextIndex = firstIndex + 1;
1894 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1895 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00001896 // FIXME: found done logic probably fails if there are more than 4
1897 // sorted angles. It should bias towards the first and last undone
1898 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001899 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00001900 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00001901 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001902 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00001903 bool altFlipped = false;
1904 bool foundFlipped = false;
1905 int foundMax = SK_MinS32;
1906 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001907 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00001908 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001909 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001910 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001911 nextIndex = 0;
1912 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001913 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00001914 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00001915 if (sumWinding) {
1916 lastNonZeroSum = sumWinding;
1917 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00001918 nextSegment = nextAngle->segment();
caryclark@google.com2ddff932012-08-07 21:25:27 +00001919 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00001920 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00001921 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00001922 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00001923 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
1924 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001925 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00001926 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00001927 if (!active) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001928 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001929 // FIXME: seems like a bug that this isn't calling userInnerWinding
caryclark@google.com47580692012-07-23 12:14:49 +00001930 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00001931 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001932 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001933 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001934 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00001935 return NULL;
1936 }
caryclark@google.com47580692012-07-23 12:14:49 +00001937 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001938 foundAngle = nextAngle;
caryclark@google.com47580692012-07-23 12:14:49 +00001939 foundDone = nextSegment->done(*nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00001940 foundFlipped = altFlipped;
1941 foundMax = maxWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001942 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001943 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001944 }
caryclark@google.com24bec792012-08-20 12:43:57 +00001945 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00001946 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001947 if (foundAngle && foundDone2) {
1948 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001949 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00001950 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00001951 foundAngle = nextAngle;
caryclark@google.com24bec792012-08-20 12:43:57 +00001952 foundDone2 = nextSegment->done(*nextAngle);
1953 foundFlipped = altFlipped;
1954 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00001955 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001956 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001957 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001958 }
1959 // if the winding is non-zero, nextAngle does not connect to
1960 // current chain. If we haven't done so already, mark the angle
1961 // as done, record the winding value, and mark connected unambiguous
1962 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001963 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001964 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00001965 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001966 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001967 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001968 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001969 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001970 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00001971 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001972 }
1973 if (last) {
1974 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001975 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001976 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001977 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00001978 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001979 if (!foundAngle) {
1980 return NULL;
1981 }
1982 nextStart = foundAngle->start();
1983 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00001984 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00001985 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001986 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
1987 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00001988 if (winding) {
1989 #if DEBUG_WINDING
1990 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
1991 if (foundSum == SK_MinS32) {
1992 SkDebugf("?");
1993 } else {
1994 SkDebugf("%d", foundSum);
1995 }
1996 SkDebugf(" foundMax=");
1997 if (foundMax == SK_MinS32) {
1998 SkDebugf("?");
1999 } else {
2000 SkDebugf("%d", foundMax);
2001 }
2002 SkDebugf("\n");
2003 #endif
2004 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002005 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002006 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002007 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002008 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002009 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002010 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002011
caryclark@google.com24bec792012-08-20 12:43:57 +00002012 Segment* findNextXor(int& nextStart, int& nextEnd) {
2013 const int startIndex = nextStart;
2014 const int endIndex = nextEnd;
2015 SkASSERT(startIndex != endIndex);
2016 int count = fTs.count();
2017 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2018 : startIndex > 0);
2019 int step = SkSign32(endIndex - startIndex);
2020 int end = nextSpan(startIndex, step);
2021 SkASSERT(end >= 0);
2022 Span* endSpan = &fTs[end];
2023 Segment* other;
2024 markDone(SkMin32(startIndex, endIndex), 1);
2025 if (isSimple(end)) {
2026 #if DEBUG_WINDING
2027 SkDebugf("%s simple\n", __FUNCTION__);
2028 #endif
2029 other = endSpan->fOther;
2030 nextStart = endSpan->fOtherIndex;
2031 double startT = other->fTs[nextStart].fT;
2032 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002033 if ((approximately_less_than_zero(startT) && step < 0)
2034 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002035 step = -step;
2036 SkDEBUGCODE(firstLoop = false;)
2037 }
2038 do {
2039 nextEnd = nextStart;
2040 do {
2041 nextEnd += step;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002042 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002043 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2044 break;
2045 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002046 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002047 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002048 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002049 SkDEBUGCODE(firstLoop = false;)
2050 step = -step;
2051 } while (true);
2052 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2053 return other;
2054 }
2055 SkTDArray<Angle> angles;
2056 SkASSERT(startIndex - endIndex != 0);
2057 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2058 addTwoAngles(startIndex, end, angles);
2059 buildAngles(end, angles);
2060 SkTDArray<Angle*> sorted;
2061 sortAngles(angles, sorted);
2062 int angleCount = angles.count();
2063 int firstIndex = findStartingEdge(sorted, startIndex, end);
2064 SkASSERT(firstIndex >= 0);
2065 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002066 debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002067 #endif
2068 SkASSERT(sorted[firstIndex]->segment() == this);
2069 int nextIndex = firstIndex + 1;
2070 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2071 const Angle* nextAngle;
2072 Segment* nextSegment;
2073 do {
2074 if (nextIndex == angleCount) {
2075 nextIndex = 0;
2076 }
2077 nextAngle = sorted[nextIndex];
2078 nextSegment = nextAngle->segment();
2079 if (!nextSegment->done(*nextAngle)) {
2080 break;
2081 }
2082 if (++nextIndex == lastIndex) {
2083 return NULL;
2084 }
2085 } while (true);
2086 nextStart = nextAngle->start();
2087 nextEnd = nextAngle->end();
2088 return nextSegment;
2089 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002090
2091 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2092 int angleCount = sorted.count();
2093 int firstIndex = -1;
2094 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2095 const Angle* angle = sorted[angleIndex];
2096 if (angle->segment() == this && angle->start() == end &&
2097 angle->end() == start) {
2098 firstIndex = angleIndex;
2099 break;
2100 }
2101 }
2102 return firstIndex;
2103 }
2104
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002105 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com235f56a2012-09-14 14:19:30 +00002106 void findTooCloseToCall(bool isXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002107 int count = fTs.count();
2108 if (count < 3) { // require t=0, x, 1 at minimum
2109 return;
2110 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002111 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002112 int moCount;
2113 Span* match;
2114 Segment* mOther;
2115 do {
2116 match = &fTs[matchIndex];
2117 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002118 // FIXME: allow quads, cubics to be near coincident?
2119 if (mOther->fVerb == SkPath::kLine_Verb) {
2120 moCount = mOther->fTs.count();
2121 if (moCount >= 3) {
2122 break;
2123 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002124 }
2125 if (++matchIndex >= count) {
2126 return;
2127 }
2128 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002129 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002130 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002131 // look for a pair of nearby T values that map to the same (x,y) value
2132 // if found, see if the pair of other segments share a common point. If
2133 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002134 for (int index = matchIndex + 1; index < count; ++index) {
2135 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002136 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002137 continue;
2138 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002139 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002140 if (tOther->fVerb != SkPath::kLine_Verb) {
2141 continue; // FIXME: allow quads, cubics to be near coincident?
2142 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002143 int toCount = tOther->fTs.count();
2144 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002145 continue;
2146 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002147 const SkPoint* testPt = &xyAtT(test);
2148 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002149 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002150 moCount = toCount;
2151 match = test;
2152 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002153 matchPt = testPt;
2154 continue;
2155 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002156 int moStart = -1;
2157 int moEnd = -1;
2158 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002159 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002160 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002161 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002162 continue;
2163 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002164 if (moSpan.fOther == this) {
2165 if (moSpan.fOtherT == match->fT) {
2166 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002167 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002168 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002169 continue;
2170 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002171 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002172 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2173 moStart = -1;
2174 break;
2175 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002176 SkASSERT(moEnd == -1);
2177 moEnd = moIndex;
2178 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002179 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002180 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002181 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002182 continue;
2183 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002184 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002185 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002186 continue;
2187 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002188 int toStart = -1;
2189 int toEnd = -1;
2190 double toStartT, toEndT;
2191 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2192 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002193 if (toSpan.fDone) {
2194 continue;
2195 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002196 if (toSpan.fOther == this) {
2197 if (toSpan.fOtherT == test->fT) {
2198 toStart = toIndex;
2199 toStartT = toSpan.fT;
2200 }
2201 continue;
2202 }
2203 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002204 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2205 moStart = -1;
2206 break;
2207 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002208 SkASSERT(toEnd == -1);
2209 toEnd = toIndex;
2210 toEndT = toSpan.fT;
2211 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002212 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002213 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2214 if (toStart <= 0 || toEnd <= 0) {
2215 continue;
2216 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002217 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002218 continue;
2219 }
2220 // test to see if the segment between there and here is linear
2221 if (!mOther->isLinear(moStart, moEnd)
2222 || !tOther->isLinear(toStart, toEnd)) {
2223 continue;
2224 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002225 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002226 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002227 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002228 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002229 // FIXME: this is bogus for multiple ops
2230 // the xorMask needs to be accumulated from the union of the two
2231 // edges -- which means that the segment must have its own copy of the mask
2232 mOther->addTCoincident(isXor, moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002233 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002234 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002235 }
2236
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002237 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2238 // and use more concise logic like the old edge walker code?
2239 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002240 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002241 // iterate through T intersections and return topmost
2242 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002243 SkASSERT(!done());
2244 int firstT;
2245 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002246 SkPoint topPt;
2247 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002248 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002249 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002250 bool lastDone = true;
2251 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002252 const Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002253 if (!span.fDone || !lastDone) {
2254 const SkPoint& intercept = xyAtT(&span);
2255 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2256 && topPt.fX > intercept.fX)) {
2257 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002258 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002259 } else if (topPt == intercept) {
2260 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002261 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002262 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002263 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002264 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002265 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002266 int step = 1;
2267 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002268 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002269 step = -1;
2270 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002271 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002272 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002273 // if the topmost T is not on end, or is three-way or more, find left
2274 // look for left-ness from tLeft to firstT (matching y of other)
2275 SkTDArray<Angle> angles;
2276 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002277 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002278 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002279 SkTDArray<Angle*> sorted;
2280 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002281 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00002282 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002283 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002284 // skip edges that have already been processed
2285 firstT = -1;
2286 Segment* leftSegment;
2287 do {
2288 const Angle* angle = sorted[++firstT];
2289 leftSegment = angle->segment();
2290 tIndex = angle->end();
2291 endIndex = angle->start();
2292 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002293 return leftSegment;
2294 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002295
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002296 // FIXME: not crazy about this
2297 // when the intersections are performed, the other index is into an
2298 // incomplete array. as the array grows, the indices become incorrect
2299 // while the following fixes the indices up again, it isn't smart about
2300 // skipping segments whose indices are already correct
2301 // assuming we leave the code that wrote the index in the first place
2302 void fixOtherTIndex() {
2303 int iCount = fTs.count();
2304 for (int i = 0; i < iCount; ++i) {
2305 Span& iSpan = fTs[i];
2306 double oT = iSpan.fOtherT;
2307 Segment* other = iSpan.fOther;
2308 int oCount = other->fTs.count();
2309 for (int o = 0; o < oCount; ++o) {
2310 Span& oSpan = other->fTs[o];
2311 if (oT == oSpan.fT && this == oSpan.fOther) {
2312 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002313 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002314 }
2315 }
2316 }
2317 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002318
caryclark@google.com495f8e42012-05-31 13:13:11 +00002319 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002320 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002321 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002322 SkASSERT(end >= 0);
2323 if (multipleSpans(end)) {
2324 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002325 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002326 const Span& endSpan = fTs[end];
2327 Segment* other = endSpan.fOther;
2328 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002329 int otherEnd = other->nextSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002330 Span* last = other->innerChaseDone(index, step, winding);
2331 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002332 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002333 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002334
caryclark@google.com59823f72012-08-09 18:17:47 +00002335 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002336 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002337 SkASSERT(end >= 0);
2338 if (multipleSpans(end)) {
2339 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002340 }
2341 const Span& endSpan = fTs[end];
2342 Segment* other = endSpan.fOther;
2343 index = endSpan.fOtherIndex;
2344 int otherEnd = other->nextSpan(index, step);
2345 int min = SkMin32(index, otherEnd);
2346 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002347 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002348 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002349 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002350 Span* last = other->innerChaseWinding(index, step, winding);
2351 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002352 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002353 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002354
caryclark@google.com235f56a2012-09-14 14:19:30 +00002355 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2356 fDoneSpans = 0;
2357 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002358 fPts = pts;
2359 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002360 }
2361
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002362 bool intersected() const {
2363 return fTs.count() > 0;
2364 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002365
2366 bool isConnected(int startIndex, int endIndex) const {
2367 return fTs[startIndex].fWindSum != SK_MinS32
2368 || fTs[endIndex].fWindSum != SK_MinS32;
2369 }
2370
caryclark@google.com235f56a2012-09-14 14:19:30 +00002371 bool isHorizontal() const {
2372 return fBounds.fTop == fBounds.fBottom;
2373 }
2374
caryclark@google.com15fa1382012-05-07 20:49:36 +00002375 bool isLinear(int start, int end) const {
2376 if (fVerb == SkPath::kLine_Verb) {
2377 return true;
2378 }
2379 if (fVerb == SkPath::kQuad_Verb) {
2380 SkPoint qPart[3];
2381 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2382 return QuadIsLinear(qPart);
2383 } else {
2384 SkASSERT(fVerb == SkPath::kCubic_Verb);
2385 SkPoint cPart[4];
2386 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2387 return CubicIsLinear(cPart);
2388 }
2389 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002390
2391 // OPTIMIZE: successive calls could start were the last leaves off
2392 // or calls could specialize to walk forwards or backwards
2393 bool isMissing(double startT) const {
2394 size_t tCount = fTs.count();
2395 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002396 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002397 return false;
2398 }
2399 }
2400 return true;
2401 }
2402
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002403 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002404 int count = fTs.count();
2405 if (count == 2) {
2406 return true;
2407 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002408 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002409 if (approximately_less_than_zero(t)) {
2410 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002411 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002412 if (approximately_greater_than_one(t)) {
2413 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002414 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002415 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002416 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002417
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002418 bool isVertical() const {
2419 return fBounds.fLeft == fBounds.fRight;
2420 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002421
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002422 SkScalar leftMost(int start, int end) const {
2423 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2424 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002425
caryclark@google.com495f8e42012-05-31 13:13:11 +00002426 // this span is excluded by the winding rule -- chase the ends
2427 // as long as they are unambiguous to mark connections as done
2428 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002429 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002430 int index = angle->start();
2431 int endIndex = angle->end();
2432 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002433 Span* last = innerChaseDone(index, step, winding);
2434 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002435 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002436 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002437
caryclark@google.com59823f72012-08-09 18:17:47 +00002438 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002439 int index = angle->start();
2440 int endIndex = angle->end();
2441 int min = SkMin32(index, endIndex);
2442 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002443 Span* last = innerChaseWinding(index, step, winding);
2444 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002445 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002446 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002447
caryclark@google.com495f8e42012-05-31 13:13:11 +00002448 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002449 // This may be called when the segment is already marked done. While this
2450 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002451 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002452 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002453 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002454 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002455 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002456 double referenceT = fTs[index].fT;
2457 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002458 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002459 markOneDone(__FUNCTION__, lesser, winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002460 }
2461 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002462 markOneDone(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002463 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002464 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002465
caryclark@google.com24bec792012-08-20 12:43:57 +00002466 void markOneDone(const char* funName, int tIndex, int winding) {
2467 Span* span = markOneWinding(funName, tIndex, winding);
2468 if (!span) {
2469 return;
2470 }
2471 span->fDone = true;
2472 fDoneSpans++;
2473 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002474
caryclark@google.com24bec792012-08-20 12:43:57 +00002475 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2476 Span& span = fTs[tIndex];
2477 if (span.fDone) {
2478 return NULL;
2479 }
2480 #if DEBUG_MARK_DONE
2481 debugShowNewWinding(funName, span, winding);
2482 #endif
2483 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002484 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002485 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002486 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002487 span.fWindSum = winding;
2488 return &span;
2489 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002490
caryclark@google.com59823f72012-08-09 18:17:47 +00002491 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002492 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002493 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002494 double referenceT = fTs[index].fT;
2495 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002496 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002497 markOneWinding(__FUNCTION__, lesser, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002498 }
2499 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002500 markOneWinding(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002501 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002502 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002503
caryclark@google.com2ddff932012-08-07 21:25:27 +00002504 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002505 int nextDoorWind = SK_MaxS32;
2506 if (tIndex > 0) {
2507 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002508 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002509 nextDoorWind = below.fWindValue;
2510 }
2511 }
2512 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2513 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002514 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002515 nextDoorWind = above.fWindValue;
2516 }
2517 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002518 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2519 const Span& below = fTs[tIndex - 1];
2520 nextDoorWind = below.fWindValue;
2521 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002522 if (nextDoorWind != SK_MaxS32) {
2523 Span& newSpan = fTs[tIndex];
2524 newSpan.fWindValue = nextDoorWind;
2525 if (!nextDoorWind) {
2526 newSpan.fDone = true;
2527 ++fDoneSpans;
2528 }
2529 }
2530 }
2531
caryclark@google.com9764cc62012-07-12 19:29:45 +00002532 // return span if when chasing, two or more radiating spans are not done
2533 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2534 // candidate and the remaining spans have windValue == 0 (canceled by
2535 // coincidence). The coincident edges could either be removed altogether,
2536 // or this code could be more complicated in detecting this case. Worth it?
2537 bool multipleSpans(int end) const {
2538 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002539 }
2540
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002541 // This has callers for two different situations: one establishes the end
2542 // of the current span, and one establishes the beginning of the next span
2543 // (thus the name). When this is looking for the end of the current span,
2544 // coincidence is found when the beginning Ts contain -step and the end
2545 // contains step. When it is looking for the beginning of the next, the
2546 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002547 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002548 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002549 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002550 int count = fTs.count();
2551 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002552 while (step > 0 ? ++to < count : --to >= 0) {
2553 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002554 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002555 continue;
2556 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002557 return to;
2558 }
2559 return -1;
2560 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002561
2562 // FIXME
2563 // this returns at any difference in T, vs. a preset minimum. It may be
2564 // that all callers to nextSpan should use this instead.
2565 int nextExactSpan(int from, int step) const {
2566 const Span& fromSpan = fTs[from];
2567 int count = fTs.count();
2568 int to = from;
2569 while (step > 0 ? ++to < count : --to >= 0) {
2570 const Span& span = fTs[to];
2571 if (span.fT == fromSpan.fT) {
2572 continue;
2573 }
2574 return to;
2575 }
2576 return -1;
2577 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002578
caryclark@google.com235f56a2012-09-14 14:19:30 +00002579 bool operand() const {
2580 return fOperand;
2581 }
2582
2583 int oppSign(int startIndex, int endIndex) const {
2584 int result = startIndex < endIndex ? -fTs[startIndex].fWindValueOpp :
2585 fTs[endIndex].fWindValueOpp;
2586#if DEBUG_WIND_BUMP
2587 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2588#endif
2589 return result;
2590 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002591
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002592 const SkPoint* pts() const {
2593 return fPts;
2594 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002595
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002596 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002597 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002598 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2599 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002600 }
2601
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002602 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002603 const Span& span(int tIndex) const {
2604 return fTs[tIndex];
2605 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002606
caryclark@google.com235f56a2012-09-14 14:19:30 +00002607 int spanSign(const Angle* angle) const {
2608 SkASSERT(angle->segment() == this);
2609 return spanSign(angle->start(), angle->end());
2610 }
2611
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002612 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002613 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue :
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002614 fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002615#if DEBUG_WIND_BUMP
2616 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2617#endif
2618 return result;
2619 }
2620
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002621 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002622 double t(int tIndex) const {
2623 return fTs[tIndex].fT;
2624 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002625
caryclark@google.com18063442012-07-25 12:05:18 +00002626 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
2627 double start) {
2628 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002629 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00002630 *outsideTs.append() = end;
2631 *outsideTs.append() = start;
2632 }
2633 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002634
caryclark@google.com24bec792012-08-20 12:43:57 +00002635 void undoneSpan(int& start, int& end) {
2636 size_t tCount = fTs.count();
2637 size_t index;
2638 for (index = 0; index < tCount; ++index) {
2639 if (!fTs[index].fDone) {
2640 break;
2641 }
2642 }
2643 SkASSERT(index < tCount - 1);
2644 start = index;
2645 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002646 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00002647 SkASSERT(index < tCount);
2648 SkASSERT(index < tCount);
2649 end = index;
2650 }
caryclark@google.com18063442012-07-25 12:05:18 +00002651
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002652 void updatePts(const SkPoint pts[]) {
2653 fPts = pts;
2654 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002655
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002656 SkPath::Verb verb() const {
2657 return fVerb;
2658 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002659
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002660 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002661 return fTs[tIndex].fWindSum;
2662 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002663
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002664 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002665 int start = angle->start();
2666 int end = angle->end();
2667 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002668 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002669 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002670
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002671 int windValue(int tIndex) const {
2672 return fTs[tIndex].fWindValue;
2673 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002674
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002675 int windValue(const Angle* angle) const {
2676 int start = angle->start();
2677 int end = angle->end();
2678 int index = SkMin32(start, end);
2679 return windValue(index);
2680 }
2681
2682 SkScalar xAtT(const Span* span) const {
2683 return xyAtT(span).fX;
2684 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002685
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002686 const SkPoint& xyAtT(int index) const {
2687 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002688 }
2689
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002690 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002691 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002692 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002693 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002694 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002695 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002696 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002697 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002698 }
2699 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002700 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002701 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002702
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002703 SkScalar yAtT(int index) const {
2704 return yAtT(&fTs[index]);
2705 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002706
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002707 SkScalar yAtT(const Span* span) const {
2708 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002709 }
2710
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002711#if DEBUG_DUMP
2712 void dump() const {
2713 const char className[] = "Segment";
2714 const int tab = 4;
2715 for (int i = 0; i < fTs.count(); ++i) {
2716 SkPoint out;
2717 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
2718 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002719 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002720 tab + sizeof(className), className, fID,
2721 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002722 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002723 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002724 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002725 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00002726 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002727 }
2728#endif
2729
caryclark@google.com47580692012-07-23 12:14:49 +00002730#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00002731 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002732 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00002733 for (int i = 0; i < fTs.count(); ++i) {
2734 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
2735 return;
2736 }
2737 }
2738 SkASSERT(0);
2739 }
2740#endif
2741
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002742#if DEBUG_DUMP
2743 int debugID() const {
2744 return fID;
2745 }
2746#endif
2747
caryclark@google.com24bec792012-08-20 12:43:57 +00002748#if DEBUG_WINDING
2749 void debugShowSums() const {
2750 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
2751 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
2752 for (int i = 0; i < fTs.count(); ++i) {
2753 const Span& span = fTs[i];
2754 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
2755 if (span.fWindSum == SK_MinS32) {
2756 SkDebugf("?");
2757 } else {
2758 SkDebugf("%d", span.fWindSum);
2759 }
2760 SkDebugf("]");
2761 }
2762 SkDebugf("\n");
2763 }
2764#endif
2765
caryclark@google.comcc905052012-07-25 20:59:42 +00002766#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002767 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00002768 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00002769 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00002770 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00002771 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
2772 }
2773 SkDebugf("\n");
2774 }
2775#endif
2776
caryclark@google.com027de222012-07-12 12:52:50 +00002777#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002778 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00002779 if (done()) {
2780 return;
2781 }
2782 for (int i = 0; i < fTs.count(); ++i) {
2783 if (fTs[i].fDone) {
2784 continue;
2785 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002786 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00002787 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2788 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2789 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2790 }
2791 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00002792 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00002793 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00002794 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002795 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
2796 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
2797 if (fTs[i].fWindSum == SK_MinS32) {
2798 SkDebugf("?");
2799 } else {
2800 SkDebugf("%d", fTs[i].fWindSum);
2801 }
2802 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00002803 }
2804 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002805
2806 // This isn't useful yet -- but leaving it in for now in case i think of something
2807 // to use it for
2808 void validateActiveSpans() const {
2809 if (done()) {
2810 return;
2811 }
2812 int tCount = fTs.count();
2813 for (int index = 0; index < tCount; ++index) {
2814 if (fTs[index].fDone) {
2815 continue;
2816 }
2817 // count number of connections which are not done
2818 int first = index;
2819 double baseT = fTs[index].fT;
2820 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
2821 --first;
2822 }
2823 int last = index;
2824 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
2825 ++last;
2826 }
2827 int connections = 0;
2828 connections += first > 0 && !fTs[first - 1].fDone;
2829 for (int test = first; test <= last; ++test) {
2830 connections += !fTs[test].fDone;
2831 const Segment* other = fTs[test].fOther;
2832 int oIndex = fTs[test].fOtherIndex;
2833 connections += !other->fTs[oIndex].fDone;
2834 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
2835 }
2836 // SkASSERT(!(connections & 1));
2837 }
2838 }
caryclark@google.com027de222012-07-12 12:52:50 +00002839#endif
2840
caryclark@google.com0c803d02012-08-06 11:15:47 +00002841#if DEBUG_MARK_DONE
2842 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
2843 const SkPoint& pt = xyAtT(&span);
2844 SkDebugf("%s id=%d", fun, fID);
2845 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2846 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2847 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2848 }
2849 SkDebugf(") t=%1.9g (%1.9g,%1.9g) newWindSum=%d windSum=",
2850 span.fT, pt.fX, pt.fY, winding);
2851 if (span.fWindSum == SK_MinS32) {
2852 SkDebugf("?");
2853 } else {
2854 SkDebugf("%d", span.fWindSum);
2855 }
2856 SkDebugf(" windValue=%d\n", span.fWindValue);
2857 }
2858#endif
2859
caryclark@google.com47580692012-07-23 12:14:49 +00002860#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002861 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002862 const int contourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002863 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00002864 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002865 int lastSum = contourWinding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002866 int windSum = lastSum - spanSign(angles[first]);
caryclark@google.com03f97062012-08-21 13:13:52 +00002867 SkDebugf("%s %s contourWinding=%d sign=%d\n", fun, __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +00002868 contourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00002869 int index = first;
2870 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00002871 do {
2872 const Angle& angle = *angles[index];
2873 const Segment& segment = *angle.segment();
2874 int start = angle.start();
2875 int end = angle.end();
2876 const Span& sSpan = segment.fTs[start];
2877 const Span& eSpan = segment.fTs[end];
2878 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002879 if (!firstTime) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002880 lastSum = windSum;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002881 windSum -= segment.spanSign(&angle);
caryclark@google.comafe56de2012-07-24 18:11:03 +00002882 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002883 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 +00002884 " sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
2885 __FUNCTION__, index, segment.fID, kLVerbStr[segment.fVerb],
2886 start, segment.xAtT(&sSpan),
2887 segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
2888 segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
2889 lastSum, windSum, useInnerWinding(lastSum, windSum)
2890 ? windSum : lastSum, mSpan.fDone);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002891#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00002892 angle.debugShow(segment.xyAtT(&sSpan));
2893#endif
caryclark@google.com47580692012-07-23 12:14:49 +00002894 ++index;
2895 if (index == angles.count()) {
2896 index = 0;
2897 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002898 if (firstTime) {
2899 firstTime = false;
2900 }
caryclark@google.com47580692012-07-23 12:14:49 +00002901 } while (index != first);
2902 }
2903#endif
2904
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002905#if DEBUG_WINDING
2906 bool debugVerifyWinding(int start, int end, int winding) const {
2907 const Span& span = fTs[SkMin32(start, end)];
2908 int spanWinding = span.fWindSum;
2909 if (spanWinding == SK_MinS32) {
2910 return true;
2911 }
2912 int spanSign = SkSign32(start - end);
2913 int signedVal = spanSign * span.fWindValue;
2914 if (signedVal < 0) {
2915 spanWinding -= signedVal;
2916 }
2917 return span.fWindSum == winding;
2918 }
2919#endif
2920
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002921private:
2922 const SkPoint* fPts;
2923 SkPath::Verb fVerb;
2924 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002925 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00002926 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00002927 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002928#if DEBUG_DUMP
2929 int fID;
2930#endif
2931};
2932
caryclark@google.comb9738012012-07-03 19:53:30 +00002933class Contour;
2934
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002935struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00002936 Contour* fContours[2];
2937 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002938 double fTs[2][2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00002939 bool fXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002940};
2941
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002942class Contour {
2943public:
2944 Contour() {
2945 reset();
2946#if DEBUG_DUMP
2947 fID = ++gContourID;
2948#endif
2949 }
2950
2951 bool operator<(const Contour& rh) const {
2952 return fBounds.fTop == rh.fBounds.fTop
2953 ? fBounds.fLeft < rh.fBounds.fLeft
2954 : fBounds.fTop < rh.fBounds.fTop;
2955 }
2956
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002957 void addCoincident(int index, Contour* other, int otherIndex,
2958 const Intersections& ts, bool swap) {
2959 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00002960 coincidence.fContours[0] = this;
2961 coincidence.fContours[1] = other;
2962 coincidence.fSegments[0] = index;
2963 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00002964 if (fSegments[index].verb() == SkPath::kLine_Verb &&
2965 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
2966 // FIXME: coincident lines use legacy Ts instead of coincident Ts
2967 coincidence.fTs[swap][0] = ts.fT[0][0];
2968 coincidence.fTs[swap][1] = ts.fT[0][1];
2969 coincidence.fTs[!swap][0] = ts.fT[1][0];
2970 coincidence.fTs[!swap][1] = ts.fT[1][1];
2971 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
2972 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
2973 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
2974 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
2975 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
2976 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
2977 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002978 coincidence.fXor = fOperand == other->fOperand ? fXor : true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002979 }
2980
2981 void addCross(const Contour* crosser) {
2982#ifdef DEBUG_CROSS
2983 for (int index = 0; index < fCrosses.count(); ++index) {
2984 SkASSERT(fCrosses[index] != crosser);
2985 }
2986#endif
2987 *fCrosses.append() = crosser;
2988 }
2989
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002990 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002991 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002992 fContainsCurves = true;
2993 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002994
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002995 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002996 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002997 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002998 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002999
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003000 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3001 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3002 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003003
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003004 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003005 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003006 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003007 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003008 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003009
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003010 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3011 containsIntercepts();
3012 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3013 }
3014
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003015 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003016 return fBounds;
3017 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003018
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003019 void collapseTriangles() {
3020 int segmentCount = fSegments.count();
3021 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3022 fSegments[sIndex].collapseTriangles(fXor);
3023 }
3024 }
3025
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003026 void complete() {
3027 setBounds();
3028 fContainsIntercepts = false;
3029 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003030
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003031 void containsIntercepts() {
3032 fContainsIntercepts = true;
3033 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003034
rmistry@google.comd6176b02012-08-23 18:14:13 +00003035 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003036 int &tIndex, double& hitT) {
3037 int segmentCount = fSegments.count();
3038 const Segment* bestSegment = NULL;
3039 for (int test = 0; test < segmentCount; ++test) {
3040 Segment* testSegment = &fSegments[test];
3041 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003042 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003043 continue;
3044 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003045 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003046 continue;
3047 }
3048 if (bounds.fLeft > basePt.fX) {
3049 continue;
3050 }
3051 if (bounds.fRight < basePt.fX) {
3052 continue;
3053 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003054 if (bounds.fLeft == bounds.fRight) {
3055 continue;
3056 }
3057 #if 0
3058 bool leftHalf = bounds.fLeft == basePt.fX;
3059 bool rightHalf = bounds.fRight == basePt.fX;
3060 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3061 basePt, leftHalf, rightHalf)) {
3062 continue;
3063 }
3064 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003065 double testHitT;
3066 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3067 if (testT >= 0) {
3068 bestSegment = testSegment;
3069 tIndex = testT;
3070 hitT = testHitT;
3071 }
3072 }
3073 return bestSegment;
3074 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003075
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003076 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003077 for (int index = 0; index < fCrosses.count(); ++index) {
3078 if (fCrosses[index] == crosser) {
3079 return true;
3080 }
3081 }
3082 return false;
3083 }
3084
caryclark@google.com235f56a2012-09-14 14:19:30 +00003085 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003086 int segmentCount = fSegments.count();
3087 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003088 fSegments[sIndex].findTooCloseToCall(fXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003089 }
3090 }
3091
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003092 void fixOtherTIndex() {
3093 int segmentCount = fSegments.count();
3094 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3095 fSegments[sIndex].fixOtherTIndex();
3096 }
3097 }
3098
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003099 void reset() {
3100 fSegments.reset();
3101 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003102 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003103 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003104
caryclark@google.com235f56a2012-09-14 14:19:30 +00003105 // FIXME: for binary ops, need to keep both ops winding contributions separately
3106 // in edge array
3107 void resolveCoincidence() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003108 int count = fCoincidences.count();
3109 for (int index = 0; index < count; ++index) {
3110 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003111 Contour* thisContour = coincidence.fContours[0];
3112 Contour* otherContour = coincidence.fContours[1];
3113 int thisIndex = coincidence.fSegments[0];
3114 int otherIndex = coincidence.fSegments[1];
3115 Segment& thisOne = thisContour->fSegments[thisIndex];
3116 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003117 #if DEBUG_CONCIDENT
3118 thisOne.debugShowTs();
3119 other.debugShowTs();
3120 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003121 double startT = coincidence.fTs[0][0];
3122 double endT = coincidence.fTs[0][1];
caryclark@google.com32546db2012-08-31 20:55:07 +00003123 bool opposite = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003124 if (startT > endT) {
3125 SkTSwap<double>(startT, endT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003126 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003127 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003128 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003129 double oStartT = coincidence.fTs[1][0];
3130 double oEndT = coincidence.fTs[1][1];
3131 if (oStartT > oEndT) {
3132 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003133 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003134 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003135 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com32546db2012-08-31 20:55:07 +00003136 if (opposite) {
caryclark@google.comb9738012012-07-03 19:53:30 +00003137 // make sure startT and endT have t entries
caryclark@google.com32546db2012-08-31 20:55:07 +00003138 SkASSERT(opposite);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003139 if (startT > 0 || oEndT < 1
3140 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3141 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003142 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003143 if (oStartT > 0 || endT < 1
3144 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3145 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003146 }
3147 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003148 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003149 if (startT > 0 || oStartT > 0
3150 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003151 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003152 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003153 if (endT < 1 || oEndT < 1
3154 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003155 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003156 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003157 thisOne.addTCoincident(coincidence.fXor, startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003158 }
caryclark@google.com47580692012-07-23 12:14:49 +00003159 #if DEBUG_CONCIDENT
3160 thisOne.debugShowTs();
3161 other.debugShowTs();
3162 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003163 }
3164 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003165
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003166 const SkTArray<Segment>& segments() {
3167 return fSegments;
3168 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003169
caryclark@google.com235f56a2012-09-14 14:19:30 +00003170 void setOperand(bool isOp) {
3171 fOperand = isOp;
3172 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003173
caryclark@google.com235f56a2012-09-14 14:19:30 +00003174 void setXor(bool isXor) {
3175 fXor = isXor;
3176 }
3177
caryclark@google.com15fa1382012-05-07 20:49:36 +00003178 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3179 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003180 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003181 // segments' top, and not a true sort, so it could be ameniable to regular
3182 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003183 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003184 int segmentCount = fSegments.count();
3185 SkASSERT(segmentCount > 0);
3186 int best = -1;
3187 Segment* bestSegment = NULL;
3188 while (++best < segmentCount) {
3189 Segment* testSegment = &fSegments[best];
3190 if (testSegment->done()) {
3191 continue;
3192 }
3193 bestSegment = testSegment;
3194 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003195 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003196 if (!bestSegment) {
3197 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003198 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003199 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003200 for (int test = best + 1; test < segmentCount; ++test) {
3201 Segment* testSegment = &fSegments[test];
3202 if (testSegment->done()) {
3203 continue;
3204 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003205 if (testSegment->bounds().fTop > bestTop) {
3206 continue;
3207 }
3208 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003209 if (bestTop > testTop) {
3210 bestTop = testTop;
3211 bestSegment = testSegment;
3212 }
3213 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003214 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003215 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003216 }
3217
caryclark@google.com24bec792012-08-20 12:43:57 +00003218 Segment* undoneSegment(int& start, int& end) {
3219 int segmentCount = fSegments.count();
3220 for (int test = 0; test < segmentCount; ++test) {
3221 Segment* testSegment = &fSegments[test];
3222 if (testSegment->done()) {
3223 continue;
3224 }
3225 testSegment->undoneSpan(start, end);
3226 return testSegment;
3227 }
3228 return NULL;
3229 }
3230
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003231 int updateSegment(int index, const SkPoint* pts) {
3232 Segment& segment = fSegments[index];
3233 segment.updatePts(pts);
3234 return segment.verb() + 1;
3235 }
3236
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003237#if DEBUG_TEST
3238 SkTArray<Segment>& debugSegments() {
3239 return fSegments;
3240 }
3241#endif
3242
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003243#if DEBUG_DUMP
3244 void dump() {
3245 int i;
3246 const char className[] = "Contour";
3247 const int tab = 4;
3248 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
3249 for (i = 0; i < fSegments.count(); ++i) {
3250 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
3251 className, i);
3252 fSegments[i].dump();
3253 }
3254 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
3255 tab + sizeof(className), className,
3256 fBounds.fLeft, fBounds.fTop,
3257 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003258 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
3259 className, fContainsIntercepts);
3260 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
3261 className, fContainsCurves);
3262 }
3263#endif
3264
caryclark@google.com027de222012-07-12 12:52:50 +00003265#if DEBUG_ACTIVE_SPANS
3266 void debugShowActiveSpans() {
3267 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003268 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00003269 }
3270 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003271
3272 void validateActiveSpans() {
3273 for (int index = 0; index < fSegments.count(); ++index) {
3274 fSegments[index].validateActiveSpans();
3275 }
3276 }
caryclark@google.com027de222012-07-12 12:52:50 +00003277#endif
3278
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003279protected:
3280 void setBounds() {
3281 int count = fSegments.count();
3282 if (count == 0) {
3283 SkDebugf("%s empty contour\n", __FUNCTION__);
3284 SkASSERT(0);
3285 // FIXME: delete empty contour?
3286 return;
3287 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003288 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003289 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003290 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003291 }
3292 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003293
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003294private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003295 SkTArray<Segment> fSegments;
3296 SkTDArray<Coincidence> fCoincidences;
3297 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003298 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003299 bool fContainsIntercepts;
3300 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003301 bool fOperand; // true for the second argument to a binary operator
3302 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003303#if DEBUG_DUMP
3304 int fID;
3305#endif
3306};
3307
3308class EdgeBuilder {
3309public:
3310
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003311EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003312 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003313 , fContours(contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003314 , fCurrentContour(NULL)
3315 , fOperand(false)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003316{
caryclark@google.com235f56a2012-09-14 14:19:30 +00003317 fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003318#if DEBUG_DUMP
3319 gContourID = 0;
3320 gSegmentID = 0;
3321#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00003322 fSecondHalf = preFetch();
3323}
3324
3325void addOperand(const SkPath& path) {
3326 fPath = &path;
3327 fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
3328 preFetch();
3329}
3330
3331void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003332 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003333 complete();
3334 if (fCurrentContour && !fCurrentContour->segments().count()) {
3335 fContours.pop_back();
3336 }
3337 // correct pointers in contours since fReducePts may have moved as it grew
3338 int cIndex = 0;
3339 int extraCount = fExtra.count();
3340 SkASSERT(extraCount == 0 || fExtra[0] == -1);
3341 int eIndex = 0;
3342 int rIndex = 0;
3343 while (++eIndex < extraCount) {
3344 int offset = fExtra[eIndex];
3345 if (offset < 0) {
3346 ++cIndex;
3347 continue;
3348 }
3349 fCurrentContour = &fContours[cIndex];
3350 rIndex += fCurrentContour->updateSegment(offset - 1,
3351 &fReducePts[rIndex]);
3352 }
3353 fExtra.reset(); // we're done with this
3354}
3355
3356ShapeOpMask xorMask() const {
3357 return fXorMask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003358}
3359
3360protected:
3361
3362void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003363 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003364 fCurrentContour->complete();
3365 fCurrentContour = NULL;
3366 }
3367}
3368
caryclark@google.com235f56a2012-09-14 14:19:30 +00003369// FIXME:remove once we can access path pts directly
3370int preFetch() {
3371 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003372 SkPoint pts[4];
3373 SkPath::Verb verb;
3374 do {
3375 verb = iter.next(pts);
3376 *fPathVerbs.append() = verb;
3377 if (verb == SkPath::kMove_Verb) {
3378 *fPathPts.append() = pts[0];
3379 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
3380 fPathPts.append(verb, &pts[1]);
3381 }
3382 } while (verb != SkPath::kDone_Verb);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003383 return fPathVerbs.count();
3384}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003385
caryclark@google.com235f56a2012-09-14 14:19:30 +00003386void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003387 SkPath::Verb reducedVerb;
3388 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003389 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003390 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003391 const SkPoint* finalCurveStart = NULL;
3392 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003393 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003394 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
3395 switch (verb) {
3396 case SkPath::kMove_Verb:
3397 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003398 if (!fCurrentContour) {
3399 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003400 fCurrentContour->setOperand(fOperand);
3401 fCurrentContour->setXor(fXorMask == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003402 *fExtra.append() = -1; // start new contour
3403 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003404 finalCurveEnd = pointsPtr++;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003405 continue;
3406 case SkPath::kLine_Verb:
3407 // skip degenerate points
3408 if (pointsPtr[-1].fX != pointsPtr[0].fX
3409 || pointsPtr[-1].fY != pointsPtr[0].fY) {
3410 fCurrentContour->addLine(&pointsPtr[-1]);
3411 }
3412 break;
3413 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00003414
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003415 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
3416 if (reducedVerb == 0) {
3417 break; // skip degenerate points
3418 }
3419 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00003420 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003421 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003422 break;
3423 }
3424 fCurrentContour->addQuad(&pointsPtr[-1]);
3425 break;
3426 case SkPath::kCubic_Verb:
3427 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
3428 if (reducedVerb == 0) {
3429 break; // skip degenerate points
3430 }
3431 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003432 *fExtra.append() =
3433 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003434 break;
3435 }
3436 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003437 *fExtra.append() =
3438 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003439 break;
3440 }
3441 fCurrentContour->addCubic(&pointsPtr[-1]);
3442 break;
3443 case SkPath::kClose_Verb:
3444 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003445 if (finalCurveStart && finalCurveEnd
3446 && *finalCurveStart != *finalCurveEnd) {
3447 *fReducePts.append() = *finalCurveStart;
3448 *fReducePts.append() = *finalCurveEnd;
3449 *fExtra.append() =
3450 fCurrentContour->addLine(fReducePts.end() - 2);
3451 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003452 complete();
3453 continue;
3454 default:
3455 SkDEBUGFAIL("bad verb");
3456 return;
3457 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003458 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003459 pointsPtr += verb;
3460 SkASSERT(fCurrentContour);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003461 if (verbPtr == endOfFirstHalf) {
3462 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003463 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003464 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003465}
3466
3467private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00003468 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003469 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003470 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003471 Contour* fCurrentContour;
3472 SkTArray<Contour>& fContours;
3473 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003474 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com235f56a2012-09-14 14:19:30 +00003475 ShapeOpMask fXorMask;
3476 int fSecondHalf;
3477 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003478};
3479
3480class Work {
3481public:
3482 enum SegmentType {
3483 kHorizontalLine_Segment = -1,
3484 kVerticalLine_Segment = 0,
3485 kLine_Segment = SkPath::kLine_Verb,
3486 kQuad_Segment = SkPath::kQuad_Verb,
3487 kCubic_Segment = SkPath::kCubic_Verb,
3488 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00003489
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003490 void addCoincident(Work& other, const Intersections& ts, bool swap) {
3491 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
3492 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003493
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003494 // FIXME: does it make sense to write otherIndex now if we're going to
3495 // fix it up later?
3496 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003497 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003498 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003499
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003500 // Avoid collapsing t values that are close to the same since
3501 // we walk ts to describe consecutive intersections. Since a pair of ts can
3502 // be nearly equal, any problems caused by this should be taken care
3503 // of later.
3504 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003505 int addT(double newT, const Work& other) {
3506 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003507 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003508
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003509 bool advance() {
3510 return ++fIndex < fLast;
3511 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003512
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003513 SkScalar bottom() const {
3514 return bounds().fBottom;
3515 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003516
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003517 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003518 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003519 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003520
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003521 const SkPoint* cubic() const {
3522 return fCubic;
3523 }
3524
3525 void init(Contour* contour) {
3526 fContour = contour;
3527 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003528 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003529 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003530
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003531 bool isAdjacent(const Work& next) {
3532 return fContour == next.fContour && fIndex + 1 == next.fIndex;
3533 }
3534
3535 bool isFirstLast(const Work& next) {
3536 return fContour == next.fContour && fIndex == 0
3537 && next.fIndex == fLast - 1;
3538 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003539
3540 SkScalar left() const {
3541 return bounds().fLeft;
3542 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003543
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003544 void promoteToCubic() {
3545 fCubic[0] = pts()[0];
3546 fCubic[2] = pts()[1];
3547 fCubic[3] = pts()[2];
3548 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
3549 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
3550 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
3551 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
3552 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003553
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003554 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003555 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003556 }
3557
3558 SkScalar right() const {
3559 return bounds().fRight;
3560 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003561
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003562 ptrdiff_t segmentIndex() const {
3563 return fIndex;
3564 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003565
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003566 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003567 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003568 SegmentType type = (SegmentType) segment.verb();
3569 if (type != kLine_Segment) {
3570 return type;
3571 }
3572 if (segment.isHorizontal()) {
3573 return kHorizontalLine_Segment;
3574 }
3575 if (segment.isVertical()) {
3576 return kVerticalLine_Segment;
3577 }
3578 return kLine_Segment;
3579 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003580
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003581 bool startAfter(const Work& after) {
3582 fIndex = after.fIndex;
3583 return advance();
3584 }
3585
3586 SkScalar top() const {
3587 return bounds().fTop;
3588 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003589
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003590 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003591 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003592 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003593
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003594 SkScalar x() const {
3595 return bounds().fLeft;
3596 }
3597
3598 bool xFlipped() const {
3599 return x() != pts()[0].fX;
3600 }
3601
3602 SkScalar y() const {
3603 return bounds().fTop;
3604 }
3605
3606 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003607 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003608 }
3609
3610protected:
3611 Contour* fContour;
3612 SkPoint fCubic[4];
3613 int fIndex;
3614 int fLast;
3615};
3616
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003617#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003618static void debugShowLineIntersection(int pts, const Work& wt,
3619 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003620 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003621 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
3622 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
3623 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
3624 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003625 return;
3626 }
3627 SkPoint wtOutPt, wnOutPt;
3628 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3629 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003630 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003631 __FUNCTION__,
3632 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3633 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
3634 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003635 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003636 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003637 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003638 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3639 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
3640 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003641 SkDebugf(" wnTs[1]=%g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003642 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003643 SkDebugf("\n");
3644}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003645#else
3646static void debugShowLineIntersection(int , const Work& ,
3647 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003648}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003649#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003650
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003651static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003652
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003653 if (test != next) {
3654 if (test->bounds().fBottom < next->bounds().fTop) {
3655 return false;
3656 }
3657 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
3658 return true;
3659 }
3660 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003661 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003662 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003663 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003664 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003665 Work wn;
3666 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003667 if (test == next && !wn.startAfter(wt)) {
3668 continue;
3669 }
3670 do {
3671 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
3672 continue;
3673 }
3674 int pts;
3675 Intersections ts;
3676 bool swap = false;
3677 switch (wt.segmentType()) {
3678 case Work::kHorizontalLine_Segment:
3679 swap = true;
3680 switch (wn.segmentType()) {
3681 case Work::kHorizontalLine_Segment:
3682 case Work::kVerticalLine_Segment:
3683 case Work::kLine_Segment: {
3684 pts = HLineIntersect(wn.pts(), wt.left(),
3685 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003686 debugShowLineIntersection(pts, wt, wn,
3687 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003688 break;
3689 }
3690 case Work::kQuad_Segment: {
3691 pts = HQuadIntersect(wn.pts(), wt.left(),
3692 wt.right(), wt.y(), wt.xFlipped(), ts);
3693 break;
3694 }
3695 case Work::kCubic_Segment: {
3696 pts = HCubicIntersect(wn.pts(), wt.left(),
3697 wt.right(), wt.y(), wt.xFlipped(), ts);
3698 break;
3699 }
3700 default:
3701 SkASSERT(0);
3702 }
3703 break;
3704 case Work::kVerticalLine_Segment:
3705 swap = true;
3706 switch (wn.segmentType()) {
3707 case Work::kHorizontalLine_Segment:
3708 case Work::kVerticalLine_Segment:
3709 case Work::kLine_Segment: {
3710 pts = VLineIntersect(wn.pts(), wt.top(),
3711 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003712 debugShowLineIntersection(pts, wt, wn,
3713 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003714 break;
3715 }
3716 case Work::kQuad_Segment: {
3717 pts = VQuadIntersect(wn.pts(), wt.top(),
3718 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3719 break;
3720 }
3721 case Work::kCubic_Segment: {
3722 pts = VCubicIntersect(wn.pts(), wt.top(),
3723 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3724 break;
3725 }
3726 default:
3727 SkASSERT(0);
3728 }
3729 break;
3730 case Work::kLine_Segment:
3731 switch (wn.segmentType()) {
3732 case Work::kHorizontalLine_Segment:
3733 pts = HLineIntersect(wt.pts(), wn.left(),
3734 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003735 debugShowLineIntersection(pts, wt, wn,
3736 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003737 break;
3738 case Work::kVerticalLine_Segment:
3739 pts = VLineIntersect(wt.pts(), wn.top(),
3740 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003741 debugShowLineIntersection(pts, wt, wn,
3742 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003743 break;
3744 case Work::kLine_Segment: {
3745 pts = LineIntersect(wt.pts(), wn.pts(), ts);
3746 debugShowLineIntersection(pts, wt, wn,
3747 ts.fT[1], ts.fT[0]);
3748 break;
3749 }
3750 case Work::kQuad_Segment: {
3751 swap = true;
3752 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
3753 break;
3754 }
3755 case Work::kCubic_Segment: {
3756 swap = true;
3757 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
3758 break;
3759 }
3760 default:
3761 SkASSERT(0);
3762 }
3763 break;
3764 case Work::kQuad_Segment:
3765 switch (wn.segmentType()) {
3766 case Work::kHorizontalLine_Segment:
3767 pts = HQuadIntersect(wt.pts(), wn.left(),
3768 wn.right(), wn.y(), wn.xFlipped(), ts);
3769 break;
3770 case Work::kVerticalLine_Segment:
3771 pts = VQuadIntersect(wt.pts(), wn.top(),
3772 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3773 break;
3774 case Work::kLine_Segment: {
3775 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
3776 break;
3777 }
3778 case Work::kQuad_Segment: {
3779 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
3780 break;
3781 }
3782 case Work::kCubic_Segment: {
3783 wt.promoteToCubic();
3784 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
3785 break;
3786 }
3787 default:
3788 SkASSERT(0);
3789 }
3790 break;
3791 case Work::kCubic_Segment:
3792 switch (wn.segmentType()) {
3793 case Work::kHorizontalLine_Segment:
3794 pts = HCubicIntersect(wt.pts(), wn.left(),
3795 wn.right(), wn.y(), wn.xFlipped(), ts);
3796 break;
3797 case Work::kVerticalLine_Segment:
3798 pts = VCubicIntersect(wt.pts(), wn.top(),
3799 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3800 break;
3801 case Work::kLine_Segment: {
3802 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
3803 break;
3804 }
3805 case Work::kQuad_Segment: {
3806 wn.promoteToCubic();
3807 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
3808 break;
3809 }
3810 case Work::kCubic_Segment: {
3811 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
3812 break;
3813 }
3814 default:
3815 SkASSERT(0);
3816 }
3817 break;
3818 default:
3819 SkASSERT(0);
3820 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003821 if (!foundCommonContour && pts > 0) {
3822 test->addCross(next);
3823 next->addCross(test);
3824 foundCommonContour = true;
3825 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003826 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00003827 if (pts == 2) {
3828 if (wn.segmentType() <= Work::kLine_Segment
3829 && wt.segmentType() <= Work::kLine_Segment) {
3830 wt.addCoincident(wn, ts, swap);
3831 continue;
3832 }
3833 if (wn.segmentType() == Work::kQuad_Segment
3834 && wt.segmentType() == Work::kQuad_Segment
3835 && ts.coincidentUsed() == 2) {
3836 wt.addCoincident(wn, ts, swap);
3837 continue;
3838 }
3839
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003840 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003841 int pt2 = 0;
3842 int pt2inc = 1;
3843 if (ts.fFlip) {
3844 pt2 = pts - 1;
3845 pt2inc = -1;
3846 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003847 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003848 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
3849 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003850 int testTAt = wt.addT(ts.fT[swap][pt], wn);
3851 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003852 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
3853 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
3854 pt2 += pt2inc;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003855 }
3856 } while (wn.advance());
3857 } while (wt.advance());
3858 return true;
3859}
3860
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003861// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003862// see if coincidence is formed by clipping non-concident segments
caryclark@google.com235f56a2012-09-14 14:19:30 +00003863static void coincidenceCheck(SkTDArray<Contour*>& contourList) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003864 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00003865 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003866 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00003867 contour->resolveCoincidence();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003868 }
3869 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
3870 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00003871 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003872 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003873#if 0
3874 // OPTIMIZATION: this check could be folded in with findTooClose -- maybe
3875 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
3876 Contour* contour = contourList[cIndex];
3877 contour->collapseTriangles();
3878 }
3879#endif
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003880}
3881
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003882// project a ray from the top of the contour up and see if it hits anything
3883// note: when we compute line intersections, we keep track of whether
3884// two contours touch, so we need only look at contours not touching this one.
3885// OPTIMIZATION: sort contourList vertically to avoid linear walk
3886static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003887 const Segment* current, int index, int endIndex) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003888 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003889 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003890 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00003891 const Segment* test = NULL;
3892 int tIndex;
3893 double tHit;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003894 // bool checkCrosses = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003895 for (int cTest = 0; cTest < contourCount; ++cTest) {
3896 Contour* contour = contourList[cTest];
3897 if (basePt.fY < contour->bounds().fTop) {
3898 continue;
3899 }
3900 if (bestY > contour->bounds().fBottom) {
3901 continue;
3902 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003903#if 0
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003904 // even though the contours crossed, if spans cancel through concidence,
3905 // the contours may be not have any span links to chase, and the current
3906 // segment may be isolated. Detect this by seeing if current has
3907 // uninitialized wind sums. If so, project a ray instead of relying on
3908 // previously found intersections.
3909 if (baseContour == contour) {
3910 continue;
3911 }
3912 if (checkCrosses && baseContour->crosses(contour)) {
3913 if (current->isConnected(index, endIndex)) {
3914 continue;
3915 }
3916 checkCrosses = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003917 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003918#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003919 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
3920 if (next) {
3921 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003922 }
caryclark@google.com47580692012-07-23 12:14:49 +00003923 }
3924 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00003925 return 0;
3926 }
3927 int winding, windValue;
3928 // If the ray hit the end of a span, we need to construct the wheel of
3929 // angles to find the span closest to the ray -- even if there are just
3930 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003931 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003932 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00003933 SkTDArray<Angle> angles;
3934 int end = test->nextSpan(tIndex, 1);
3935 if (end < 0) {
3936 end = test->nextSpan(tIndex, -1);
3937 }
3938 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00003939 SkASSERT(angles.count() > 0);
3940 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
3941#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00003942 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00003943#endif
3944 return 0;
3945 }
caryclark@google.com47580692012-07-23 12:14:49 +00003946 test->buildAngles(tIndex, angles);
3947 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00003948 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00003949 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00003950 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00003951 // hour after 6 o'clock
3952 sortAngles(angles, sorted);
3953#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00003954 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00003955#endif
3956 // walk the sorted angle fan to find the lowest angle
3957 // above the base point. Currently, the first angle in the sorted array
3958 // is 12 noon or an earlier hour (the next counterclockwise)
3959 int count = sorted.count();
3960 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003961 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00003962 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003963 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00003964 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00003965 angle = sorted[index];
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003966 if (baseMatches && angle->isHorizontal()) {
3967 continue;
3968 }
3969 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00003970 test = angle->segment();
3971 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
3972 const SkPoint* pts = test->pts();
3973 indexDx = pts[2].fX - pts[1].fX - indexDx;
3974 }
caryclark@google.com47580692012-07-23 12:14:49 +00003975 if (indexDx < 0) {
3976 left = index;
3977 } else if (indexDx > 0) {
3978 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00003979 int previous = index - 1;
3980 if (previous < 0) {
3981 previous = count - 1;
3982 }
3983 const Angle* prev = sorted[previous];
3984 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
3985#if DEBUG_SORT
3986 SkDebugf("%s use prev\n", __FUNCTION__);
3987#endif
3988 right = previous;
3989 }
caryclark@google.com47580692012-07-23 12:14:49 +00003990 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003991 } else {
3992 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003993 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003994 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003995 if (left < 0 && right < 0) {
3996 left = mid;
3997 }
caryclark@google.com47580692012-07-23 12:14:49 +00003998 SkASSERT(left >= 0 || right >= 0);
3999 if (left < 0) {
4000 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004001 } else if (left >= 0 && mid >= 0 && right >= 0
4002 && sorted[mid]->sign() == sorted[right]->sign()) {
4003 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004004 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004005 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004006 test = angle->segment();
4007 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004008 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004009 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004010#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004011 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4012 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004013#endif
4014 } else {
4015 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004016 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004017 windValue = test->windValue(tIndex);
4018#if DEBUG_WINDING
4019 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4020 windValue);
4021#endif
4022 }
4023 // see if a + change in T results in a +/- change in X (compute x'(T))
4024 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004025 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4026 const SkPoint* pts = test->pts();
4027 dx = pts[2].fX - pts[1].fX - dx;
4028 }
caryclark@google.com47580692012-07-23 12:14:49 +00004029#if DEBUG_WINDING
4030 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4031#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004032 SkASSERT(dx != 0);
4033 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004034 winding += dx > 0 ? -windValue : windValue;
4035#if DEBUG_WINDING
4036 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4037#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004038 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004039 return winding;
4040}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004041
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004042// OPTIMIZATION: not crazy about linear search here to find top active y.
4043// seems like we should break down and do the sort, or maybe sort each
rmistry@google.comd6176b02012-08-23 18:14:13 +00004044// contours' segments?
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004045// Once the segment array is built, there's no reason I can think of not to
4046// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004047// FIXME: return the contour found to pass to inner contour check
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004048static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004049 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004050 int cIndex = 0;
4051 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004052 SkScalar bestY = SK_ScalarMax;
4053 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004054 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004055 contour = contourList[cIndex];
4056 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004057 } while (!topStart && ++cIndex < contourCount);
4058 if (!topStart) {
4059 return NULL;
4060 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004061 while (++cIndex < contourCount) {
4062 contour = contourList[cIndex];
4063 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004064 continue;
4065 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004066 SkScalar testY = SK_ScalarMax;
4067 Segment* test = contour->topSegment(testY);
4068 if (!test || bestY <= testY) {
4069 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004070 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004071 topStart = test;
4072 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004073 }
4074 return topStart;
4075}
4076
caryclark@google.com24bec792012-08-20 12:43:57 +00004077static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4078 int contourCount = contourList.count();
4079 Segment* result;
4080 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4081 Contour* contour = contourList[cIndex];
4082 result = contour->undoneSegment(start, end);
4083 if (result) {
4084 return result;
4085 }
4086 }
4087 return NULL;
4088}
4089
4090
4091
caryclark@google.come21cb182012-07-23 21:26:31 +00004092static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
4093 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004094 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004095 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004096 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4097 Segment* segment = backPtr.fOther;
4098 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004099 SkTDArray<Angle> angles;
4100 int done = 0;
4101 if (segment->activeAngle(tIndex, done, angles)) {
4102 Angle* last = angles.end() - 1;
4103 tIndex = last->start();
4104 endIndex = last->end();
4105 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004106 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004107 if (done == angles.count()) {
4108 chase.pop(&span);
4109 continue;
4110 }
4111 SkTDArray<Angle*> sorted;
4112 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004113#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004114 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004115#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004116 // find first angle, initialize winding to computed fWindSum
4117 int firstIndex = -1;
4118 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004119 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004120 do {
4121 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004122 segment = angle->segment();
4123 winding = segment->windSum(angle);
4124 } while (winding == SK_MinS32);
4125 int spanWinding = segment->spanSign(angle->start(), angle->end());
4126 #if DEBUG_WINDING
4127 SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
4128 __FUNCTION__, winding, spanWinding, contourWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00004129 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004130 // turn swinding into contourWinding
4131 if (spanWinding * winding < 0) {
4132 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004133 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004134 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004135 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004136 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004137 // we care about first sign and whether wind sum indicates this
4138 // edge is inside or outside. Maybe need to pass span winding
4139 // or first winding or something into this function?
4140 // advance to first undone angle, then return it and winding
4141 // (to set whether edges are active or not)
4142 int nextIndex = firstIndex + 1;
4143 int angleCount = sorted.count();
4144 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004145 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00004146 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00004147 do {
4148 SkASSERT(nextIndex != firstIndex);
4149 if (nextIndex == angleCount) {
4150 nextIndex = 0;
4151 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004152 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00004153 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004154 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00004155 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004156 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00004157 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
4158 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004159 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004160 tIndex = angle->start();
4161 endIndex = angle->end();
4162 int lesser = SkMin32(tIndex, endIndex);
4163 const Span& nextSpan = segment->span(lesser);
4164 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004165#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00004166 // FIXME: this be wrong. assign startWinding if edge is in
4167 // same direction. If the direction is opposite, winding to
4168 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00004169 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004170 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004171 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004172 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004173#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004174 break;
4175 }
4176 } while (++nextIndex != lastIndex);
4177 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004178 }
4179 return NULL;
4180}
4181
caryclark@google.com027de222012-07-12 12:52:50 +00004182#if DEBUG_ACTIVE_SPANS
4183static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004184 int index;
4185 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00004186 contourList[index]->debugShowActiveSpans();
4187 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004188 for (index = 0; index < contourList.count(); ++ index) {
4189 contourList[index]->validateActiveSpans();
4190 }
caryclark@google.com027de222012-07-12 12:52:50 +00004191}
4192#endif
4193
caryclark@google.com27c449a2012-07-27 18:26:38 +00004194static bool windingIsActive(int winding, int spanWinding) {
4195 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
4196 && (!winding || !spanWinding || winding == -spanWinding);
4197}
4198
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004199// Each segment may have an inside or an outside. Segments contained within
4200// winding may have insides on either side, and form a contour that should be
4201// ignored. Segments that are coincident with opposing direction segments may
4202// have outsides on either side, and should also disappear.
4203// 'Normal' segments will have one inside and one outside. Subsequent connections
4204// when winding should follow the intersection direction. If more than one edge
4205// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004206 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00004207 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com24bec792012-08-20 12:43:57 +00004208static void bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004209 bool firstContour = true;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004210 do {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004211 Segment* topStart = findTopContour(contourList);
caryclark@google.com15fa1382012-05-07 20:49:36 +00004212 if (!topStart) {
4213 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00004214 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004215 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00004216 // follow edges to intersection by changing the index by direction.
4217 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004218 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004219 int contourWinding;
4220 if (firstContour) {
4221 contourWinding = 0;
4222 firstContour = false;
4223 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004224 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004225 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00004226 if (sumWinding == SK_MinS32) {
4227 sumWinding = current->computeSum(index, endIndex);
4228 }
4229 if (sumWinding == SK_MinS32) {
4230 contourWinding = innerContourCheck(contourList, current,
4231 index, endIndex);
4232 } else {
4233 contourWinding = sumWinding;
4234 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004235 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
4236 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00004237 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004238 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004239#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004240 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004241 sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00004242 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004243#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004244 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004245#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004246 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004247 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
4248#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004249 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004250 SkPoint lastPt;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004251 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004252 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004253 // FIXME: needs work. While it works in limited situations, it does
4254 // not always compute winding correctly. Active should be removed and instead
4255 // the initial winding should be correctly passed in so that if the
4256 // inner contour is wound the same way, it never finds an accumulated
4257 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00004258 // other than zero when resolving sorted angles.
caryclark@google.com27c449a2012-07-27 18:26:38 +00004259 bool active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004260 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004261 do {
caryclark@google.com0e08a192012-07-13 21:07:52 +00004262 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00004263 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
4264 __FUNCTION__, active ? "true" : "false",
4265 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004266 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004267 const SkPoint* firstPt = NULL;
4268 do {
4269 SkASSERT(!current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004270 int nextStart = index;
4271 int nextEnd = endIndex;
4272 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.com27c449a2012-07-27 18:26:38 +00004273 nextStart, nextEnd, winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004274 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00004275 if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
4276 lastPt = current->addCurveTo(index, endIndex, simple, true);
4277 SkASSERT(*firstPt == lastPt);
4278 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004279 break;
4280 }
4281 if (!firstPt) {
4282 firstPt = &current->addMoveTo(index, simple, active);
4283 }
4284 lastPt = current->addCurveTo(index, endIndex, simple, active);
4285 current = next;
4286 index = nextStart;
4287 endIndex = nextEnd;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004288 } while (*firstPt != lastPt && (active || !current->done()));
4289 if (firstPt && active) {
4290 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004291 SkDebugf("path.close();\n");
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004292 #endif
4293 simple.close();
4294 }
caryclark@google.come21cb182012-07-23 21:26:31 +00004295 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004296 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00004297 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004298 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004299 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00004300 break;
4301 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004302 int lesser = SkMin32(index, endIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004303 spanWinding = current->spanSign(index, endIndex);
4304 winding = current->windSum(lesser);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004305 bool inner = useInnerWinding(winding - spanWinding, winding);
4306 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004307 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
caryclark@google.com59823f72012-08-09 18:17:47 +00004308 " inner=%d result=%d\n",
caryclark@google.com2ddff932012-08-07 21:25:27 +00004309 __FUNCTION__, current->debugID(), current->t(lesser),
4310 spanWinding, winding, SkSign32(index - endIndex),
4311 useInnerWinding(winding - spanWinding, winding),
caryclark@google.com2ddff932012-08-07 21:25:27 +00004312 inner ? winding - spanWinding : winding);
4313 #endif
4314 if (inner) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004315 winding -= spanWinding;
4316 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004317 active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004318 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004319 } while (true);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004320}
4321
caryclark@google.com24bec792012-08-20 12:43:57 +00004322static void bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
4323 Segment* current;
4324 int start, end;
4325 while ((current = findUndone(contourList, start, end))) {
4326 const SkPoint* firstPt = NULL;
4327 SkPoint lastPt;
4328 do {
4329 SkASSERT(!current->done());
4330 int nextStart = start;
4331 int nextEnd = end;
4332 Segment* next = current->findNextXor(nextStart, nextEnd);
4333 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00004334 if (firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
4335 lastPt = current->addCurveTo(start, end, simple, true);
4336 SkASSERT(*firstPt == lastPt);
4337 }
caryclark@google.com24bec792012-08-20 12:43:57 +00004338 break;
4339 }
4340 if (!firstPt) {
4341 firstPt = &current->addMoveTo(start, simple, true);
4342 }
4343 lastPt = current->addCurveTo(start, end, simple, true);
4344 current = next;
4345 start = nextStart;
4346 end = nextEnd;
4347 } while (*firstPt != lastPt);
4348 if (firstPt) {
4349 #if DEBUG_PATH_CONSTRUCTION
4350 SkDebugf("%s close\n", __FUNCTION__);
4351 #endif
4352 simple.close();
4353 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004354 #if DEBUG_ACTIVE_SPANS
4355 debugShowActiveSpans(contourList);
4356 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00004357 }
caryclark@google.com24bec792012-08-20 12:43:57 +00004358}
4359
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004360static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
4361 int contourCount = contourList.count();
4362 for (int cTest = 0; cTest < contourCount; ++cTest) {
4363 Contour* contour = contourList[cTest];
4364 contour->fixOtherTIndex();
4365 }
4366}
4367
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004368static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004369 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004370 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004371 if (count == 0) {
4372 return;
4373 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004374 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004375 *list.append() = &contours[index];
4376 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004377 QSort<Contour>(list.begin(), list.end() - 1);
4378}
4379
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004380void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004381 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004382 simple.reset();
4383 simple.setFillType(SkPath::kEvenOdd_FillType);
4384
4385 // turn path into list of segments
4386 SkTArray<Contour> contours;
4387 // FIXME: add self-intersecting cubics' T values to segment
4388 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004389 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004390 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004391 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004392 Contour** currentPtr = contourList.begin();
4393 if (!currentPtr) {
4394 return;
4395 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004396 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004397 // find all intersections between segments
4398 do {
4399 Contour** nextPtr = currentPtr;
4400 Contour* current = *currentPtr++;
4401 Contour* next;
4402 do {
4403 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004404 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004405 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004406 // eat through coincident edges
caryclark@google.com235f56a2012-09-14 14:19:30 +00004407 coincidenceCheck(contourList);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004408 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004409 // construct closed contours
caryclark@google.com235f56a2012-09-14 14:19:30 +00004410 if (builder.xorMask() == kWinding_Mask) {
caryclark@google.com24bec792012-08-20 12:43:57 +00004411 bridgeWinding(contourList, simple);
4412 } else {
4413 bridgeXor(contourList, simple);
4414 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004415}
4416