blob: dbb8988965033c248317dc2fef6b6f540a34205f [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
caryclark@google.com47580692012-07-23 12:14:49 +000020// An Edge is a Segment generated from a Span
caryclark@google.com15fa1382012-05-07 20:49:36 +000021
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022// FIXME: remove once debugging is complete
caryclark@google.com47580692012-07-23 12:14:49 +000023#ifdef SK_DEBUG
24int gDebugMaxWindSum = SK_MaxS32;
25int gDebugMaxWindValue = SK_MaxS32;
26#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +000027
caryclark@google.coma461ff02012-10-11 12:54:23 +000028#define PRECISE_T_SORT 1
29
caryclark@google.com47580692012-07-23 12:14:49 +000030#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa0588f2012-04-26 21:01:06 +000031
caryclark@google.coma461ff02012-10-11 12:54:23 +000032#if 1 // set to 1 for multiple thread -- no debugging
caryclark@google.com47580692012-07-23 12:14:49 +000033
34const bool gRunTestsInOneThread = false;
35
36#define DEBUG_ACTIVE_SPANS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000037#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000038#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000039#define DEBUG_ANGLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000040#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000041#define DEBUG_CROSS 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000042#define DEBUG_MARK_DONE 0
caryclark@google.coma461ff02012-10-11 12:54:23 +000043#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com47580692012-07-23 12:14:49 +000044#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000045#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000046#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000047
48#else
49
caryclark@google.com47580692012-07-23 12:14:49 +000050const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000051
caryclark@google.comc91dfe42012-10-16 12:06:27 +000052#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000053#define DEBUG_ADD_INTERSECTING_TS 1
54#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000055#define DEBUG_ANGLE 1
56#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000057#define DEBUG_CROSS 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000058#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000059#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000060#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000061#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000062#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000063
64#endif
65
caryclark@google.com6aea33f2012-10-09 14:11:58 +000066#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000067
caryclark@google.comfa0588f2012-04-26 21:01:06 +000068#if DEBUG_DUMP
69static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000070// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000071static int gContourID;
72static int gSegmentID;
73#endif
74
caryclark@google.com8dcf1142012-07-02 20:27:02 +000075#ifndef DEBUG_TEST
76#define DEBUG_TEST 0
77#endif
78
caryclark@google.com32546db2012-08-31 20:55:07 +000079#define MAKE_CONST_LINE(line, pts) \
80 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
81#define MAKE_CONST_QUAD(quad, pts) \
82 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
83 {pts[2].fX, pts[2].fY}}
84#define MAKE_CONST_CUBIC(cubic, pts) \
85 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
86 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
87
caryclark@google.comfa0588f2012-04-26 21:01:06 +000088static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
89 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000090 MAKE_CONST_LINE(aLine, a);
91 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +000092 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
93}
94
95static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
96 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000097 MAKE_CONST_QUAD(aQuad, a);
98 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +000099 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000100}
101
caryclark@google.com32546db2012-08-31 20:55:07 +0000102static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000103 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000104 MAKE_CONST_CUBIC(aCubic, a);
105 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000106 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
107}
108
109static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
110 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000111 MAKE_CONST_QUAD(aQuad, a);
112 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000113#define TRY_QUARTIC_SOLUTION 1
114#if TRY_QUARTIC_SOLUTION
115 intersect2(aQuad, bQuad, intersections);
116#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000117 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000118#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000119 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000120}
121
122static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
123 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000124 MAKE_CONST_CUBIC(aCubic, a);
125 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000126 intersect(aCubic, bCubic, intersections);
127 return intersections.fUsed;
128}
129
130static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
131 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000132 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000133 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
134}
135
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000136static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
137 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000138 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000139 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
140}
141
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000142static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
143 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000144 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000145 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
146}
147
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000148static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
149 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000150 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000151 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
152}
153
154static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
155 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000156 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000157 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
158}
159
160static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
161 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000162 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000163 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000164}
165
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000166static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
167 SkScalar , SkScalar , bool , Intersections& ) = {
168 NULL,
169 VLineIntersect,
170 VQuadIntersect,
171 VCubicIntersect
172};
173
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000174static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000175 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000176 double x, y;
177 xy_at_t(line, t, x, y);
178 out->fX = SkDoubleToScalar(x);
179 out->fY = SkDoubleToScalar(y);
180}
181
182static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000183 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000184 double x, y;
185 xy_at_t(quad, t, x, y);
186 out->fX = SkDoubleToScalar(x);
187 out->fY = SkDoubleToScalar(y);
188}
189
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000190static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
191 MAKE_CONST_QUAD(quad, a);
192 xy_at_t(quad, t, out->x, out->y);
193}
194
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000195static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000196 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000197 double x, y;
198 xy_at_t(cubic, t, x, y);
199 out->fX = SkDoubleToScalar(x);
200 out->fY = SkDoubleToScalar(y);
201}
202
203static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
204 NULL,
205 LineXYAtT,
206 QuadXYAtT,
207 CubicXYAtT
208};
209
210static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000211 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000212 double x;
213 xy_at_t(aLine, t, x, *(double*) 0);
214 return SkDoubleToScalar(x);
215}
216
217static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000218 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000219 double x;
220 xy_at_t(quad, t, x, *(double*) 0);
221 return SkDoubleToScalar(x);
222}
223
224static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000225 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000226 double x;
227 xy_at_t(cubic, t, x, *(double*) 0);
228 return SkDoubleToScalar(x);
229}
230
231static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
232 NULL,
233 LineXAtT,
234 QuadXAtT,
235 CubicXAtT
236};
237
238static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000239 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000240 double y;
241 xy_at_t(aLine, t, *(double*) 0, y);
242 return SkDoubleToScalar(y);
243}
244
245static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000246 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000247 double y;
248 xy_at_t(quad, t, *(double*) 0, y);
249 return SkDoubleToScalar(y);
250}
251
252static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000253 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000254 double y;
255 xy_at_t(cubic, t, *(double*) 0, y);
256 return SkDoubleToScalar(y);
257}
258
259static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
260 NULL,
261 LineYAtT,
262 QuadYAtT,
263 CubicYAtT
264};
265
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000266static SkScalar LineDXAtT(const SkPoint a[2], double ) {
267 return a[1].fX - a[0].fX;
268}
269
270static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000271 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000272 double x;
273 dxdy_at_t(quad, t, x, *(double*) 0);
274 return SkDoubleToScalar(x);
275}
276
277static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000278 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000279 double x;
280 dxdy_at_t(cubic, t, x, *(double*) 0);
281 return SkDoubleToScalar(x);
282}
283
284static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
285 NULL,
286 LineDXAtT,
287 QuadDXAtT,
288 CubicDXAtT
289};
290
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000291static void LineSubDivide(const SkPoint a[2], double startT, double endT,
292 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000293 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000294 _Line dst;
295 sub_divide(aLine, startT, endT, dst);
296 sub[0].fX = SkDoubleToScalar(dst[0].x);
297 sub[0].fY = SkDoubleToScalar(dst[0].y);
298 sub[1].fX = SkDoubleToScalar(dst[1].x);
299 sub[1].fY = SkDoubleToScalar(dst[1].y);
300}
301
302static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
303 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000304 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000305 Quadratic dst;
306 sub_divide(aQuad, startT, endT, dst);
307 sub[0].fX = SkDoubleToScalar(dst[0].x);
308 sub[0].fY = SkDoubleToScalar(dst[0].y);
309 sub[1].fX = SkDoubleToScalar(dst[1].x);
310 sub[1].fY = SkDoubleToScalar(dst[1].y);
311 sub[2].fX = SkDoubleToScalar(dst[2].x);
312 sub[2].fY = SkDoubleToScalar(dst[2].y);
313}
314
315static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
316 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000317 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000318 Cubic dst;
319 sub_divide(aCubic, startT, endT, dst);
320 sub[0].fX = SkDoubleToScalar(dst[0].x);
321 sub[0].fY = SkDoubleToScalar(dst[0].y);
322 sub[1].fX = SkDoubleToScalar(dst[1].x);
323 sub[1].fY = SkDoubleToScalar(dst[1].y);
324 sub[2].fX = SkDoubleToScalar(dst[2].x);
325 sub[2].fY = SkDoubleToScalar(dst[2].y);
326 sub[3].fX = SkDoubleToScalar(dst[3].x);
327 sub[3].fY = SkDoubleToScalar(dst[3].y);
328}
329
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000330static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
331 SkPoint []) = {
332 NULL,
333 LineSubDivide,
334 QuadSubDivide,
335 CubicSubDivide
336};
337
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000338static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000339 _Line sub) {
340 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000341 _Line dst;
342 sub_divide(aLine, startT, endT, dst);
343 sub[0] = dst[0];
344 sub[1] = dst[1];
345}
346
347static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000348 Quadratic sub) {
349 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000350 Quadratic dst;
351 sub_divide(aQuad, startT, endT, dst);
352 sub[0] = dst[0];
353 sub[1] = dst[1];
354 sub[2] = dst[2];
355}
356
357static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000358 Cubic sub) {
359 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000360 Cubic dst;
361 sub_divide(aCubic, startT, endT, dst);
362 sub[0] = dst[0];
363 sub[1] = dst[1];
364 sub[2] = dst[2];
365 sub[3] = dst[3];
366}
367
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000368#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000369static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
370 SkRect& bounds) {
371 SkPoint dst[3];
372 QuadSubDivide(a, startT, endT, dst);
373 bounds.fLeft = bounds.fRight = dst[0].fX;
374 bounds.fTop = bounds.fBottom = dst[0].fY;
375 for (int index = 1; index < 3; ++index) {
376 bounds.growToInclude(dst[index].fX, dst[index].fY);
377 }
378}
379
380static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
381 SkRect& bounds) {
382 SkPoint dst[4];
383 CubicSubDivide(a, startT, endT, dst);
384 bounds.fLeft = bounds.fRight = dst[0].fX;
385 bounds.fTop = bounds.fBottom = dst[0].fY;
386 for (int index = 1; index < 4; ++index) {
387 bounds.growToInclude(dst[index].fX, dst[index].fY);
388 }
389}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000390#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000391
caryclark@google.com15fa1382012-05-07 20:49:36 +0000392static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000393 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000394 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000395 Quadratic dst;
396 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000397 if (order == 2) { // quad became line
398 for (int index = 0; index < order; ++index) {
399 SkPoint* pt = reducePts.append();
400 pt->fX = SkDoubleToScalar(dst[index].x);
401 pt->fY = SkDoubleToScalar(dst[index].y);
402 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000403 }
404 return (SkPath::Verb) (order - 1);
405}
406
407static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
408 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000409 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000410 Cubic dst;
411 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000412 if (order == 2 || order == 3) { // cubic became line or quad
413 for (int index = 0; index < order; ++index) {
414 SkPoint* pt = reducePts.append();
415 pt->fX = SkDoubleToScalar(dst[index].x);
416 pt->fY = SkDoubleToScalar(dst[index].y);
417 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000418 }
419 return (SkPath::Verb) (order - 1);
420}
421
caryclark@google.com15fa1382012-05-07 20:49:36 +0000422static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000423 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000424 return isLinear(aQuad, 0, 2);
425}
426
427static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000428 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000429 return isLinear(aCubic, 0, 3);
430}
431
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000432static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000433 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000434 double x[2];
435 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000436 xy_at_t(aLine, endT, x[1], *(double*) 0);
437 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000438}
439
440static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000441 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000442 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000443}
444
445static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000446 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000447 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000448}
449
450static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
451 NULL,
452 LineLeftMost,
453 QuadLeftMost,
454 CubicLeftMost
455};
456
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000457#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000458static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
459 Intersections& intersections) {
460 MAKE_CONST_QUAD(aQuad, a);
461 MAKE_CONST_LINE(bLine, b);
462 return intersectRay(aQuad, bLine, intersections);
463}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000464#endif
465
466static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
467 Intersections& intersections) {
468 MAKE_CONST_QUAD(aQuad, a);
469 return intersectRay(aQuad, bLine, intersections);
470}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000471
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000472class Segment;
473
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000474struct Span {
475 Segment* fOther;
476 mutable SkPoint fPt; // lazily computed as needed
477 double fT;
478 double fOtherT; // value at fOther[fOtherIndex].fT
479 int fOtherIndex; // can't be used during intersection
480 int fWindSum; // accumulated from contours surrounding this one
481 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
482 int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
483 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000484 bool fUnsortableStart; // set when start is part of an unsortable pair
485 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000486};
487
caryclark@google.com15fa1382012-05-07 20:49:36 +0000488// sorting angles
489// given angles of {dx dy ddx ddy dddx dddy} sort them
490class Angle {
491public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000492 // FIXME: this is bogus for quads and cubics
493 // if the quads and cubics' line from end pt to ctrl pt are coincident,
494 // there's no obvious way to determine the curve ordering from the
495 // derivatives alone. In particular, if one quadratic's coincident tangent
496 // is longer than the other curve, the final control point can place the
497 // longer curve on either side of the shorter one.
498 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
499 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000500
caryclark@google.com32546db2012-08-31 20:55:07 +0000501 // start here
502 /*(
503 for quads and cubics, set up a parameterized line (e.g. LineParameters )
504 for points [0] to [1]. See if point [2] is on that line, or on one side
505 or the other. If it both quads' end points are on the same side, choose
506 the shorter tangent. If the tangents are equal, choose the better second
507 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000508
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000509 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000510 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000511 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000512 double y = dy();
513 double ry = rh.dy();
514 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
515 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000516 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000517 double x = dx();
518 double rx = rh.dx();
519 if (y == 0 && ry == 0 && x * rx < 0) {
520 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000521 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000522 double x_ry = x * ry;
523 double rx_y = rx * y;
524 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000525 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000526 return cmp < 0;
527 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000528 if (approximately_zero(x_ry) && approximately_zero(rx_y)
529 && !approximately_zero_squared(cmp)) {
530 return cmp < 0;
531 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000532 // at this point, the initial tangent line is coincident
533 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) || !approximately_zero(rh.fSide))) {
534 // FIXME: running demo will trigger this assertion
535 // (don't know if commenting out will trigger further assertion or not)
536 // commenting it out allows demo to run in release, though
537 // SkASSERT(fSide != rh.fSide);
538 return fSide < rh.fSide;
539 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000540 // see if either curve can be lengthened and try the tangent compare again
541 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
542 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
543 Angle longer = *this;
544 Angle rhLonger = rh;
545 if (longer.lengthen() | rhLonger.lengthen()) {
546 return longer < rhLonger;
547 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000548 // what if we extend in the other direction?
549 longer = *this;
550 rhLonger = rh;
551 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
552 return longer < rhLonger;
553 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000554 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000555 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
556 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000557 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000558 // end of the shorter tangent to midway between the end points
559 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000560 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
561 double len = fTangent1.normalSquared();
562 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000563 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000564 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000565 int roots, rroots;
566 bool flip = false;
567 do {
568 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
569 double midX = (q[0].x + q[2].x) / 2;
570 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000571 ray[0] = q[1];
572 ray[1].x = midX;
573 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000574 SkASSERT(ray[0] != ray[1]);
575 roots = QuadRayIntersect(fPts, ray, i);
576 rroots = QuadRayIntersect(rh.fPts, ray, ri);
577 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000578 if (roots == 0 || rroots == 0) {
579 // FIXME: we don't have a solution in this case. The interim solution
580 // is to mark the edges as unsortable, exclude them from this and
581 // future computations, and allow the returned path to be fragmented
582 fUnsortable = true;
583 rh.fUnsortable = true;
584 return this < &rh; // even with no solution, return a stable sort
585 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000586 _Point loc;
587 double best = SK_ScalarInfinity;
588 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000589 int index;
590 for (index = 0; index < roots; ++index) {
591 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000592 dx = loc.x - ray[0].x;
593 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000594 dist = dx * dx + dy * dy;
595 if (best > dist) {
596 best = dist;
597 }
598 }
599 for (index = 0; index < rroots; ++index) {
600 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000601 dx = loc.x - ray[0].x;
602 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000603 dist = dx * dx + dy * dy;
604 if (best > dist) {
605 return fSide < 0;
606 }
607 }
608 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000609 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000610
caryclark@google.com47580692012-07-23 12:14:49 +0000611 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000612 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000613 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000614
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000615 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000616 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000617 }
618
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000619 int end() const {
620 return fEnd;
621 }
622
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000623 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000624 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000625 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000626
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000627 bool lengthen() {
628 int newEnd = fEnd;
629 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
630 fEnd = newEnd;
631 setSpans();
632 return true;
633 }
634 return false;
635 }
636
caryclark@google.coma461ff02012-10-11 12:54:23 +0000637 bool reverseLengthen() {
638 if (fReversed) {
639 return false;
640 }
641 int newEnd = fStart;
642 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
643 fEnd = newEnd;
644 fReversed = true;
645 setSpans();
646 return true;
647 }
648 return false;
649 }
650
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000651 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000652 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000653 fSegment = segment;
654 fStart = start;
655 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000656 fPts = orig;
657 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000658 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000659 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000660 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000661 setSpans();
662 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000663
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000664 void setSpans() {
665 double startT = (*fSpans)[fStart].fT;
666 double endT = (*fSpans)[fEnd].fT;
667 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000668 case SkPath::kLine_Verb:
669 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000670 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000671 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
672 fTangent1.lineEndPoints(l);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000673 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000674 break;
675 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000676 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000677 fTangent1.quadEndPoints(fQ, 0, 1);
678 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000679 break;
680 case SkPath::kCubic_Verb:
681 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000682 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000683 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000684 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000685 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000686 default:
687 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000688 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000689 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000690
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000691 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000692 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000693 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000694
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000695 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000696 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000697 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000698
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000699 const SkTDArray<Span>* spans() const {
700 return fSpans;
701 }
702
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000703 int start() const {
704 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000705 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000706
707 bool unsortable() const {
708 return fUnsortable;
709 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000710
caryclark@google.comc899ad92012-08-23 15:24:42 +0000711#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000712 const SkPoint* pts() const {
713 return fPts;
714 }
715
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000716 SkPath::Verb verb() const {
717 return fVerb;
718 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000719
caryclark@google.comc899ad92012-08-23 15:24:42 +0000720 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000721 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000722 }
723#endif
724
caryclark@google.com15fa1382012-05-07 20:49:36 +0000725private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000726 const SkPoint* fPts;
727 Quadratic fQ;
728 SkPath::Verb fVerb;
729 double fSide;
730 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000731 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000732 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000733 int fStart;
734 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000735 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000736 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000737};
738
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000739// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000740struct Bounds : public SkRect {
741 static bool Intersects(const Bounds& a, const Bounds& b) {
742 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
743 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
744 }
745
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000746 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
747 if (left < fLeft) {
748 fLeft = left;
749 }
750 if (top < fTop) {
751 fTop = top;
752 }
753 if (right > fRight) {
754 fRight = right;
755 }
756 if (bottom > fBottom) {
757 fBottom = bottom;
758 }
759 }
760
761 void add(const Bounds& toAdd) {
762 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
763 }
764
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000765 bool isEmpty() {
766 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000767 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000768 || isnan(fLeft) || isnan(fRight)
769 || isnan(fTop) || isnan(fBottom);
770 }
771
772 void setCubicBounds(const SkPoint a[4]) {
773 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000774 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000775 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000776 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
777 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000778 }
779
780 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000781 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000782 _Rect dRect;
783 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000784 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
785 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000786 }
787};
788
caryclark@google.com2ddff932012-08-07 21:25:27 +0000789static bool useInnerWinding(int outerWinding, int innerWinding) {
790 SkASSERT(outerWinding != innerWinding);
791 int absOut = abs(outerWinding);
792 int absIn = abs(innerWinding);
793 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
794 if (outerWinding * innerWinding < 0) {
795#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000796 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000797 outerWinding, innerWinding, result ? "true" : "false");
798#endif
799 }
800 return result;
801}
802
caryclark@google.com235f56a2012-09-14 14:19:30 +0000803static const bool opLookup[][2][2] = {
804 // ==0 !=0
805 // b a b a
806 {{true , false}, {false, true }}, // a - b
807 {{false, false}, {true , true }}, // a & b
808 {{true , true }, {false, false}}, // a | b
809 {{true , true }, {true , true }}, // a ^ b
810};
811
812static bool activeOp(bool angleIsOp, int otherNonZero, ShapeOp op) {
813 return opLookup[op][otherNonZero][angleIsOp];
814}
815
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000816class Segment {
817public:
818 Segment() {
819#if DEBUG_DUMP
820 fID = ++gSegmentID;
821#endif
822 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000823
caryclark@google.com9764cc62012-07-12 19:29:45 +0000824 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
825 if (activeAngleInner(index, done, angles)) {
826 return true;
827 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000828 double referenceT = fTs[index].fT;
829 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000830 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000831 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000832 return true;
833 }
834 }
835 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000836 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000837 return true;
838 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000839 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000840 return false;
841 }
842
caryclark@google.com9764cc62012-07-12 19:29:45 +0000843 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000844 Span* span = &fTs[index];
845 Segment* other = span->fOther;
846 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +0000847 return other->activeAngleInner(oIndex, done, angles);
848 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000849
caryclark@google.com9764cc62012-07-12 19:29:45 +0000850 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
851 int next = nextSpan(index, 1);
852 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000853 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000854 if (upSpan.fWindValue) {
855 addAngle(angles, index, next);
856 if (upSpan.fDone) {
857 done++;
858 } else if (upSpan.fWindSum != SK_MinS32) {
859 return true;
860 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000861 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000862 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000863 int prev = nextSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000864 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +0000865 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000866 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000867 if (downSpan.fWindValue) {
868 addAngle(angles, index, prev);
869 if (downSpan.fDone) {
870 done++;
871 } else if (downSpan.fWindSum != SK_MinS32) {
872 return true;
873 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000874 }
875 }
876 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000877 }
878
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000879 SkScalar activeTop() const {
880 SkASSERT(!done());
881 int count = fTs.count();
882 SkScalar result = SK_ScalarMax;
883 bool lastDone = true;
884 for (int index = 0; index < count; ++index) {
885 bool done = fTs[index].fDone;
886 if (!done || !lastDone) {
887 SkScalar y = yAtT(index);
888 if (result > y) {
889 result = y;
890 }
891 }
892 lastDone = done;
893 }
894 SkASSERT(result < SK_ScalarMax);
895 return result;
896 }
897
898 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000899 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000900 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000901#if DEBUG_ANGLE
902 if (angles.count() > 1) {
903 SkPoint angle0Pt, newPt;
904 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
905 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
906 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
907 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
908 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
909 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000910#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000911 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000912 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000913
caryclark@google.com2ddff932012-08-07 21:25:27 +0000914 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +0000915 double oEnd) {
916 int tIndex = -1;
917 int tCount = fTs.count();
918 int oIndex = -1;
919 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +0000920 do {
921 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000922 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +0000923 int tIndexStart = tIndex;
924 do {
925 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000926 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +0000927 int oIndexStart = oIndex;
928 double nextT;
929 do {
930 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000931 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000932 double oNextT;
933 do {
934 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000935 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000936 // at this point, spans before and after are at:
937 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
938 // if tIndexStart == 0, no prior span
939 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +0000940
caryclark@google.comcc905052012-07-25 20:59:42 +0000941 // advance the span with zero winding
942 // if the following span exists (not past the end, non-zero winding)
943 // connect the two edges
944 if (!fTs[tIndexStart].fWindValue) {
945 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
946 #if DEBUG_CONCIDENT
947 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
948 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000949 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
950 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +0000951 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000952 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000953 }
954 if (nextT < 1 && fTs[tIndex].fWindValue) {
955 #if DEBUG_CONCIDENT
956 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
957 __FUNCTION__, fID, other.fID, tIndex,
958 fTs[tIndex].fT, xyAtT(tIndex).fX,
959 xyAtT(tIndex).fY);
960 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000961 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000962 }
963 } else {
964 SkASSERT(!other.fTs[oIndexStart].fWindValue);
965 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
966 #if DEBUG_CONCIDENT
967 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
968 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000969 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
970 other.xyAtT(oIndexStart).fY);
971 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +0000972 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +0000973 }
974 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
975 #if DEBUG_CONCIDENT
976 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
977 __FUNCTION__, fID, other.fID, oIndex,
978 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
979 other.xyAtT(oIndex).fY);
980 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
981 #endif
982 }
983 }
984 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000985
caryclark@google.comcc905052012-07-25 20:59:42 +0000986 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
987 double oEnd) {
988 // walk this to outsideTs[0]
989 // walk other to outsideTs[1]
990 // if either is > 0, add a pointer to the other, copying adjacent winding
991 int tIndex = -1;
992 int oIndex = -1;
993 double tStart = outsideTs[0];
994 double oStart = outsideTs[1];
995 do {
996 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000997 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +0000998 do {
999 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001000 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001001 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001002 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001003 }
1004 tStart = fTs[tIndex].fT;
1005 oStart = other.fTs[oIndex].fT;
1006 do {
1007 double nextT;
1008 do {
1009 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001010 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001011 tStart = nextT;
1012 do {
1013 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001014 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001015 oStart = nextT;
1016 if (tStart == 1 && oStart == 1) {
1017 break;
1018 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001019 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001020 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001021 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001022
caryclark@google.com235f56a2012-09-14 14:19:30 +00001023 void addCubic(const SkPoint pts[4], bool operand) {
1024 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001025 fBounds.setCubicBounds(pts);
1026 }
1027
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001028 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001029 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001030 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001031 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001032 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001033 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001034 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001035 SkDebugf("path.%sTo(%1.9g,%1.9g",
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001036 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
1037 if (fVerb > 1) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001038 SkDebugf(", %1.9g,%1.9g", edge[2].fX, edge[2].fY);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001039 }
1040 if (fVerb > 2) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001041 SkDebugf(", %1.9g,%1.9g", edge[3].fX, edge[3].fY);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001042 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001043 SkDebugf(");\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001044 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001045 switch (fVerb) {
1046 case SkPath::kLine_Verb:
1047 path.lineTo(edge[1].fX, edge[1].fY);
1048 break;
1049 case SkPath::kQuad_Verb:
1050 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
1051 break;
1052 case SkPath::kCubic_Verb:
1053 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
1054 edge[3].fX, edge[3].fY);
1055 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001056 default:
1057 SkASSERT(0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001058 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001059 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001060 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001061 }
1062
caryclark@google.com235f56a2012-09-14 14:19:30 +00001063 void addLine(const SkPoint pts[2], bool operand) {
1064 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001065 fBounds.set(pts, 2);
1066 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001067
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001068 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
1069 const SkPoint& pt = xyAtT(tIndex);
1070 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001071 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001072 SkDebugf("path.moveTo(%1.9g, %1.9g);\n", pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001073 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001074 path.moveTo(pt.fX, pt.fY);
1075 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001076 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001077 }
1078
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001079 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001080 void addOtherT(int index, double otherT, int otherIndex) {
1081 Span& span = fTs[index];
1082 span.fOtherT = otherT;
1083 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001084 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001085
caryclark@google.com235f56a2012-09-14 14:19:30 +00001086 void addQuad(const SkPoint pts[3], bool operand) {
1087 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001088 fBounds.setQuadBounds(pts);
1089 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001090
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001091 // Defer all coincident edge processing until
1092 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001093
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001094// no need to be tricky; insert in normal T order
1095// resolve overlapping ts when considering coincidence later
1096
1097 // add non-coincident intersection. Resulting edges are sorted in T.
1098 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001099 // FIXME: in the pathological case where there is a ton of intercepts,
1100 // binary search?
1101 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001102 size_t tCount = fTs.count();
caryclark@google.comc899ad92012-08-23 15:24:42 +00001103 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001104 if (approximately_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001105 newT = 0;
1106 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001107 if (approximately_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001108 newT = 1;
1109 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001110 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001111 // OPTIMIZATION: if there are three or more identical Ts, then
1112 // the fourth and following could be further insertion-sorted so
1113 // that all the edges are clockwise or counterclockwise.
1114 // This could later limit segment tests to the two adjacent
1115 // neighbors, although it doesn't help with determining which
1116 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001117 if (newT < fTs[index].fT) {
1118 insertedAt = index;
1119 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001120 }
1121 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001122 Span* span;
1123 if (insertedAt >= 0) {
1124 span = fTs.insert(insertedAt);
1125 } else {
1126 insertedAt = tCount;
1127 span = fTs.append();
1128 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001129 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001130 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001131 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001132 span->fWindSum = SK_MinS32;
1133 span->fWindValue = 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001134 span->fWindValueOpp = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001135 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001136 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001137 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001138 span->fUnsortableStart = false;
1139 span->fUnsortableEnd = false;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001140 return insertedAt;
1141 }
1142
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001143 // set spans from start to end to decrement by one
1144 // note this walks other backwards
1145 // FIMXE: there's probably an edge case that can be constructed where
1146 // two span in one segment are separated by float epsilon on one span but
1147 // not the other, if one segment is very small. For this
1148 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001149 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001150 // sorted? It feels better in such a case to match the span's other span
1151 // pointer since both coincident segments must contain the same spans.
1152 void addTCancel(double startT, double endT, Segment& other,
1153 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001154 SkASSERT(!approximately_negative(endT - startT));
1155 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001156 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001157 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001158 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001159 ++index;
1160 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001161 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001162 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001163 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001164 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001165 Span* test = &fTs[index];
1166 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001167 SkTDArray<double> outsideTs;
1168 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001169 do {
1170 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001171 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001172 double testT = test->fT;
1173 double oTestT = oTest->fT;
1174 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001175 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001176 if (decrement) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001177 if (binary) {
1178 --(span->fWindValueOpp);
1179 } else {
1180 decrementSpan(span);
1181 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001182 } else if (track && span->fT < 1 && oTestT < 1) {
1183 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001184 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001185 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001186 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001187 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001188 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1189 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1190 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001191 while (approximately_negative(otherTMatchStart - oSpan->fT)
1192 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001193 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001194 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001195 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001196 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001197 other.decrementSpan(oSpan);
1198 } else if (track && oSpan->fT < 1 && testT < 1) {
1199 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001200 }
1201 if (!oIndex) {
1202 break;
1203 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001204 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001205 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001206 test = span;
1207 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001208 } while (!approximately_negative(endT - test->fT));
1209 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001210 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001211 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001212 double tStart = outsideTs[0];
1213 double oStart = outsideTs[1];
1214 addCancelOutsides(tStart, oStart, other, oEndT);
1215 int count = outsideTs.count();
1216 if (count > 2) {
1217 double tStart = outsideTs[count - 2];
1218 double oStart = outsideTs[count - 1];
1219 addCancelOutsides(tStart, oStart, other, oEndT);
1220 }
caryclark@google.com18063442012-07-25 12:05:18 +00001221 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001222 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001223 double tStart = oOutsideTs[0];
1224 double oStart = oOutsideTs[1];
1225 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001226 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001227 }
1228
1229 // set spans from start to end to increment the greater by one and decrement
1230 // the lesser
caryclark@google.com235f56a2012-09-14 14:19:30 +00001231 void addTCoincident(const bool isXor, double startT, double endT,
1232 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001233 SkASSERT(!approximately_negative(endT - startT));
1234 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001235 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001236 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001237 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001238 ++index;
1239 }
1240 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001241 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001242 ++oIndex;
1243 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001244 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001245 Span* test = &fTs[index];
1246 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001247 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001248 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001249 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001250 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001251 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001252 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001253 bool decrementThis = (test->fWindValue < oTest->fWindValue) & !isXor;
1254 bool decrementOther = (test->fWindValue >= oTest->fWindValue) & !isXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001255 Span* end = test;
1256 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001257 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001258 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001259 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001260 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001261 if (transfer) {
1262 if (decrementOther) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001263 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001264 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001265 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001266 if (binary) {
1267 ++(end->fWindValueOpp);
1268 } else {
1269 ++(end->fWindValue);
1270 }
caryclark@google.com18063442012-07-25 12:05:18 +00001271 } else if (decrementSpan(end)) {
1272 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001273 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001274 } else if (oTest->fWindValue) {
1275 SkASSERT(!decrementOther);
1276 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1277 TrackOutside(xOutsideTs, end->fT, oStartT);
1278 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001279 }
1280 end = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001281 } while (approximately_negative(end->fT - test->fT));
caryclark@google.com59823f72012-08-09 18:17:47 +00001282 // because of the order in which coincidences are resolved, this and other
1283 // may not have the same intermediate points. Compute the corresponding
1284 // intermediate T values (using this as the master, other as the follower)
1285 // and walk other conditionally -- hoping that it catches up in the end
1286 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001287 Span* oEnd = oTest;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001288 while (!approximately_negative(oEndT - oEnd->fT) && approximately_negative(oEnd->fT - otherTMatch)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00001289 if (transfer) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001290 if (decrementThis) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001291 #ifdef SK_DEBUG
1292 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1293 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001294 if (binary) {
1295 ++(oEnd->fWindValueOpp);
1296 } else {
1297 ++(oEnd->fWindValue);
1298 }
caryclark@google.com18063442012-07-25 12:05:18 +00001299 } else if (other.decrementSpan(oEnd)) {
1300 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001301 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001302 } else if (test->fWindValue) {
1303 SkASSERT(!decrementOther);
1304 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1305 SkASSERT(0); // track for later?
1306 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001307 }
1308 oEnd = &other.fTs[++oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001309 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001310 test = end;
1311 oTest = oEnd;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001312 } while (!approximately_negative(endT - test->fT));
1313 SkASSERT(approximately_negative(oTest->fT - oEndT));
1314 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001315 if (!done()) {
1316 if (outsideTs.count()) {
1317 addCoinOutsides(outsideTs, other, oEndT);
1318 }
1319 if (xOutsideTs.count()) {
1320 addCoinOutsides(xOutsideTs, other, oEndT);
1321 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001322 }
1323 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001324 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001325 }
1326 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001327
caryclark@google.comcc905052012-07-25 20:59:42 +00001328 // FIXME: this doesn't prevent the same span from being added twice
1329 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001330 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001331 int tCount = fTs.count();
1332 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1333 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001334 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001335 break;
1336 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001337 if (approximately_negative(span.fT - t) && span.fOther == &other
1338 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001339#if DEBUG_ADD_T_PAIR
1340 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1341 __FUNCTION__, fID, t, other.fID, otherT);
1342#endif
1343 return;
1344 }
1345 }
caryclark@google.com47580692012-07-23 12:14:49 +00001346#if DEBUG_ADD_T_PAIR
1347 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1348 __FUNCTION__, fID, t, other.fID, otherT);
1349#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001350 int insertedAt = addT(t, &other);
1351 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001352 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001353 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001354 matchWindingValue(insertedAt, t, borrowWind);
1355 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001356 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001357
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001358 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001359 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001360 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1361 addAngle(angles, end, start);
1362 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001363 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001364 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001365 int tIndex = nextExactSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001366 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001367 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001368 }
1369 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001370
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001371 const Bounds& bounds() const {
1372 return fBounds;
1373 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001374
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001375 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001376 double referenceT = fTs[index].fT;
1377 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001378 #if PRECISE_T_SORT
1379 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
1380 buildAnglesInner(lesser, angles);
1381 }
1382 do {
1383 buildAnglesInner(index, angles);
1384 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
1385 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001386 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001387 buildAnglesInner(lesser, angles);
1388 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001389 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001390 buildAnglesInner(index, angles);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001391 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00001392 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001393 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001394
1395 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1396 Span* span = &fTs[index];
1397 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001398 // if there is only one live crossing, and no coincidence, continue
1399 // in the same direction
1400 // if there is coincidence, the only choice may be to reverse direction
1401 // find edge on either side of intersection
1402 int oIndex = span->fOtherIndex;
1403 // if done == -1, prior span has already been processed
1404 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001405 #if PRECISE_T_SORT
1406 int next = other->nextExactSpan(oIndex, step);
1407 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001408 int next = other->nextSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001409 #endif
1410 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001411 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001412 #if PRECISE_T_SORT
1413 next = other->nextExactSpan(oIndex, step);
1414 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001415 next = other->nextSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001416 #endif
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001417 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001418 // add candidate into and away from junction
1419 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001420 }
1421
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001422 // figure out if the segment's ascending T goes clockwise or not
1423 // not enough context to write this as shown
1424 // instead, add all segments meeting at the top
1425 // sort them using buildAngleList
1426 // find the first in the sort
1427 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001428 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001429 SkASSERT(0); // incomplete
1430 return false;
1431 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001432
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001433 // FIXME may not need this at all
1434 // FIXME once I figure out the logic, merge this and too close to call
1435 // NOTE not sure if tiny triangles can ever form at the edge, so until we
1436 // see one, only worry about triangles that happen away from 0 and 1
1437 void collapseTriangles(bool isXor) {
1438 if (fTs.count() < 3) { // require t=0, x, 1 at minimum
1439 return;
1440 }
1441 int lastIndex = 1;
1442 double lastT;
1443 while (approximately_less_than_zero((lastT = fTs[lastIndex].fT))) {
1444 ++lastIndex;
1445 }
1446 if (approximately_greater_than_one(lastT)) {
1447 return;
1448 }
1449 int matchIndex = lastIndex;
1450 do {
1451 Span& match = fTs[++matchIndex];
1452 double matchT = match.fT;
1453 if (approximately_greater_than_one(matchT)) {
1454 return;
1455 }
1456 if (matchT == lastT) {
1457 goto nextSpan;
1458 }
1459 if (approximately_negative(matchT - lastT)) {
1460 Span& last = fTs[lastIndex];
1461 Segment* lOther = last.fOther;
1462 double lT = last.fOtherT;
1463 if (approximately_less_than_zero(lT) || approximately_greater_than_one(lT)) {
1464 goto nextSpan;
1465 }
1466 Segment* mOther = match.fOther;
1467 double mT = match.fOtherT;
1468 if (approximately_less_than_zero(mT) || approximately_greater_than_one(mT)) {
1469 goto nextSpan;
1470 }
1471 // add new point to connect adjacent spurs
1472 int count = lOther->fTs.count();
1473 for (int index = 0; index < count; ++index) {
1474 Span& span = lOther->fTs[index];
1475 if (span.fOther == mOther && approximately_zero(span.fOtherT - mT)) {
1476 goto nextSpan;
1477 }
1478 }
1479 mOther->addTPair(mT, *lOther, lT, false);
1480 // FIXME ? this could go on to detect that spans on mOther, lOther are now coincident
1481 }
1482 nextSpan:
1483 lastIndex = matchIndex;
1484 lastT = matchT;
1485 } while (true);
1486 }
1487
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001488 int computeSum(int startIndex, int endIndex) {
1489 SkTDArray<Angle> angles;
1490 addTwoAngles(startIndex, endIndex, angles);
1491 buildAngles(endIndex, angles);
caryclark@google.comd1688742012-09-18 20:08:37 +00001492 // OPTIMIZATION: check all angles to see if any have computed wind sum
1493 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001494 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001495 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001496#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00001497 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001498#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001499 if (!sortable) {
1500 return SK_MinS32;
1501 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001502 int angleCount = angles.count();
1503 const Angle* angle;
1504 const Segment* base;
1505 int winding;
1506 int firstIndex = 0;
1507 do {
1508 angle = sorted[firstIndex];
1509 base = angle->segment();
1510 winding = base->windSum(angle);
1511 if (winding != SK_MinS32) {
1512 break;
1513 }
1514 if (++firstIndex == angleCount) {
1515 return SK_MinS32;
1516 }
1517 } while (true);
1518 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001519 int spanWinding = base->spanSign(angle);
1520 bool inner = useInnerWinding(winding + spanWinding, winding);
1521 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001522 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001523 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001524 inner ? winding + spanWinding : winding);
1525 #endif
1526 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001527 winding += spanWinding;
1528 }
1529 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001530 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001531 #endif
1532 int nextIndex = firstIndex + 1;
1533 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001534 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001535 do {
1536 if (nextIndex == angleCount) {
1537 nextIndex = 0;
1538 }
1539 angle = sorted[nextIndex];
1540 Segment* segment = angle->segment();
1541 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001542 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001543 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001544 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001545 maxWinding = winding;
1546 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001547 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001548 }
1549 } while (++nextIndex != lastIndex);
1550 return windSum(SkMin32(startIndex, endIndex));
1551 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001552
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001553 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001554 int bestT = -1;
1555 SkScalar top = bounds().fTop;
1556 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001557 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001558 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001559 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001560 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001561 if (fTs[start].fWindValue == 0) {
1562 continue;
1563 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001564 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001565 double startT = fTs[start].fT;
1566 double endT = fTs[end].fT;
1567 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001568 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001569 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001570 // FIXME: always use original and limit results to T values within
1571 // start t and end t.
1572 // OPTIMIZE: use specialty function that intersects ray with curve,
1573 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001574 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1575 false, intersections);
1576 if (pts == 0) {
1577 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001578 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001579 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1580 // if the intersection is edge on, wait for another one
1581 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001582 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001583 for (int index = 0; index < pts; ++index) {
1584 SkPoint pt;
1585 double foundT = intersections.fT[0][index];
1586 double testT = startT + (endT - startT) * foundT;
1587 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1588 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001589 if (fVerb > SkPath::kLine_Verb
1590 && !approximately_less_than_zero(foundT)
1591 && !approximately_greater_than_one(foundT)) {
1592 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1593 if (approximately_zero(dx)) {
1594 continue;
1595 }
1596 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001597 bestY = pt.fY;
1598 bestT = foundT < 1 ? start : end;
1599 hitT = testT;
1600 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001601 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001602 } while (fTs[end].fT != 1);
1603 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001604 }
caryclark@google.com18063442012-07-25 12:05:18 +00001605
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001606 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1607 // if a segment is connected to this one, consider it crossing
1608 int tIndex;
1609 if (fPts[0].fX == basePt.fX) {
1610 tIndex = 0;
1611 do {
1612 const Span& sSpan = fTs[tIndex];
1613 const Segment* sOther = sSpan.fOther;
1614 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1615 continue;
1616 }
1617 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1618 : sOther->fBounds.fRight > basePt.fX) {
1619 return true;
1620 }
1621 } while (fTs[++tIndex].fT == 0);
1622 }
1623 if (fPts[fVerb].fX == basePt.fX) {
1624 tIndex = fTs.count() - 1;
1625 do {
1626 const Span& eSpan = fTs[tIndex];
1627 const Segment* eOther = eSpan.fOther;
1628 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1629 continue;
1630 }
1631 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1632 : eOther->fBounds.fRight > basePt.fX) {
1633 return true;
1634 }
1635 } while (fTs[--tIndex].fT == 1);
1636 }
1637 return false;
1638 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001639
caryclark@google.com18063442012-07-25 12:05:18 +00001640 bool decrementSpan(Span* span) {
1641 SkASSERT(span->fWindValue > 0);
1642 if (--(span->fWindValue) == 0) {
1643 span->fDone = true;
1644 ++fDoneSpans;
1645 return true;
1646 }
1647 return false;
1648 }
1649
caryclark@google.com15fa1382012-05-07 20:49:36 +00001650 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001651 SkASSERT(fDoneSpans <= fTs.count());
1652 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001653 }
1654
caryclark@google.com47580692012-07-23 12:14:49 +00001655 bool done(const Angle& angle) const {
1656 int start = angle.start();
1657 int end = angle.end();
1658 const Span& mSpan = fTs[SkMin32(start, end)];
1659 return mSpan.fDone;
1660 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00001661
caryclark@google.com235f56a2012-09-14 14:19:30 +00001662 Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001663 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
1664 bool& unsortable, ShapeOp op,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001665 const int aXorMask, const int bXorMask) {
1666 const int startIndex = nextStart;
1667 const int endIndex = nextEnd;
1668 int outerWinding = winding;
1669 int innerWinding = winding + spanWinding;
1670 #if DEBUG_WINDING
1671 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1672 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
1673 #endif
1674 if (useInnerWinding(outerWinding, innerWinding)) {
1675 outerWinding = innerWinding;
1676 }
1677 SkASSERT(startIndex != endIndex);
1678 int count = fTs.count();
1679 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1680 : startIndex > 0);
1681 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001682 #if PRECISE_T_SORT
1683 int end = nextExactSpan(startIndex, step);
1684 #else
caryclark@google.com235f56a2012-09-14 14:19:30 +00001685 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001686 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001687 SkASSERT(end >= 0);
1688 Span* endSpan = &fTs[end];
1689 Segment* other;
1690 if (isSimple(end)) {
1691 // mark the smaller of startIndex, endIndex done, and all adjacent
1692 // spans with the same T value (but not 'other' spans)
1693 #if DEBUG_WINDING
1694 SkDebugf("%s simple\n", __FUNCTION__);
1695 #endif
1696 markDone(SkMin32(startIndex, endIndex), outerWinding);
1697 other = endSpan->fOther;
1698 nextStart = endSpan->fOtherIndex;
1699 double startT = other->fTs[nextStart].fT;
1700 nextEnd = nextStart;
1701 do {
1702 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001703 }
1704 #if PRECISE_T_SORT
1705 while (precisely_zero(startT - other->fTs[nextEnd].fT));
1706 #else
1707 while (approximately_zero(startT - other->fTs[nextEnd].fT));
1708 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001709 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1710 return other;
1711 }
1712 // more than one viable candidate -- measure angles to find best
1713 SkTDArray<Angle> angles;
1714 SkASSERT(startIndex - endIndex != 0);
1715 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1716 addTwoAngles(startIndex, end, angles);
1717 buildAngles(end, angles);
1718 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001719 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001720 int angleCount = angles.count();
1721 int firstIndex = findStartingEdge(sorted, startIndex, end);
1722 SkASSERT(firstIndex >= 0);
1723 #if DEBUG_SORT
1724 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
1725 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001726 if (!sortable) {
1727 unsortable = true;
1728 return NULL;
1729 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001730 SkASSERT(sorted[firstIndex]->segment() == this);
1731 #if DEBUG_WINDING
1732 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
1733 #endif
1734 int aSumWinding = winding;
1735 int bSumWinding = winding;
1736 bool angleIsOp = sorted[firstIndex]->segment()->operand();
1737 int angleSpan = spanSign(sorted[firstIndex]);
1738 if (angleIsOp) {
1739 bSumWinding -= angleSpan;
1740 } else {
1741 aSumWinding -= angleSpan;
1742 }
1743 int nextIndex = firstIndex + 1;
1744 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1745 const Angle* foundAngle = NULL;
1746 // FIXME: found done logic probably fails if there are more than 4
1747 // sorted angles. It should bias towards the first and last undone
1748 // edges -- but not sure that it won't choose a middle (incorrect)
1749 // edge if one is undone
1750 bool foundDone = false;
1751 bool foundDone2 = false;
1752 // iterate through the angle, and compute everyone's winding
1753 bool altFlipped = false;
1754 bool foundFlipped = false;
1755 int foundMax = SK_MinS32;
1756 int foundSum = SK_MinS32;
1757 Segment* nextSegment;
1758 int lastNonZeroSum = winding;
1759 do {
1760 if (nextIndex == angleCount) {
1761 nextIndex = 0;
1762 }
1763 const Angle* nextAngle = sorted[nextIndex];
1764 nextSegment = nextAngle->segment();
1765 angleIsOp = nextSegment->operand();
1766 int sumWinding = angleIsOp ? bSumWinding : aSumWinding;
1767 int maxWinding = sumWinding;
1768 if (sumWinding) {
1769 lastNonZeroSum = sumWinding;
1770 }
1771 sumWinding -= nextSegment->spanSign(nextAngle);
1772 int xorMask = nextSegment->operand() ? bXorMask : aXorMask;
1773 bool otherNonZero;
1774 if (angleIsOp) {
1775 bSumWinding = sumWinding;
1776 otherNonZero = aSumWinding & aXorMask;
1777 } else {
1778 aSumWinding = sumWinding;
1779 otherNonZero = bSumWinding & bXorMask;
1780 }
1781 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00001782 #if 0 && DEBUG_WINDING
caryclark@google.com235f56a2012-09-14 14:19:30 +00001783 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
1784 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
1785 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
1786 #endif
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00001787
caryclark@google.com235f56a2012-09-14 14:19:30 +00001788 if (!(sumWinding & xorMask) && activeOp(angleIsOp, otherNonZero, op)) {
1789 if (!active) {
1790 markDone(SkMin32(startIndex, endIndex), outerWinding);
1791 // FIXME: seems like a bug that this isn't calling userInnerWinding
1792 nextSegment->markWinding(SkMin32(nextAngle->start(),
1793 nextAngle->end()), maxWinding);
1794 #if DEBUG_WINDING
1795 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
1796 #endif
1797 return NULL;
1798 }
1799 if (!foundAngle || foundDone) {
1800 foundAngle = nextAngle;
1801 foundDone = nextSegment->done(*nextAngle);
1802 foundFlipped = altFlipped;
1803 foundMax = maxWinding;
1804 }
1805 continue;
1806 }
1807 if (!(maxWinding & xorMask) && (!foundAngle || foundDone2)
1808 && activeOp(angleIsOp, otherNonZero, op)) {
1809 #if DEBUG_WINDING
1810 if (foundAngle && foundDone2) {
1811 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
1812 }
1813 #endif
1814 foundAngle = nextAngle;
1815 foundDone2 = nextSegment->done(*nextAngle);
1816 foundFlipped = altFlipped;
1817 foundSum = sumWinding;
1818 }
1819 if (nextSegment->done()) {
1820 continue;
1821 }
1822 // if the winding is non-zero, nextAngle does not connect to
1823 // current chain. If we haven't done so already, mark the angle
1824 // as done, record the winding value, and mark connected unambiguous
1825 // segments as well.
1826 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
1827 if (useInnerWinding(maxWinding, sumWinding)) {
1828 maxWinding = sumWinding;
1829 }
1830 Span* last;
1831 if (foundAngle) {
1832 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
1833 } else {
1834 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
1835 }
1836 if (last) {
1837 *chase.append() = last;
1838 }
1839 }
1840 } while (++nextIndex != lastIndex);
1841 markDone(SkMin32(startIndex, endIndex), outerWinding);
1842 if (!foundAngle) {
1843 return NULL;
1844 }
1845 nextStart = foundAngle->start();
1846 nextEnd = foundAngle->end();
1847 nextSegment = foundAngle->segment();
1848 int flipped = foundFlipped ? -1 : 1;
1849 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
1850 SkMin32(nextStart, nextEnd));
1851 if (winding) {
1852 #if DEBUG_WINDING
1853 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
1854 if (foundSum == SK_MinS32) {
1855 SkDebugf("?");
1856 } else {
1857 SkDebugf("%d", foundSum);
1858 }
1859 SkDebugf(" foundMax=");
1860 if (foundMax == SK_MinS32) {
1861 SkDebugf("?");
1862 } else {
1863 SkDebugf("%d", foundMax);
1864 }
1865 SkDebugf("\n");
1866 #endif
1867 winding = foundSum;
1868 }
1869 #if DEBUG_WINDING
1870 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
1871 #endif
1872 return nextSegment;
1873 }
caryclark@google.com47580692012-07-23 12:14:49 +00001874
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001875 // so the span needs to contain the pairing info found here
1876 // this should include the winding computed for the edge, and
1877 // what edge it connects to, and whether it is discarded
1878 // (maybe discarded == abs(winding) > 1) ?
1879 // only need derivatives for duration of sorting, add a new struct
1880 // for pairings, remove extra spans that have zero length and
1881 // reference an unused other
1882 // for coincident, the last span on the other may be marked done
1883 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001884
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001885 // if loop is exhausted, contour may be closed.
1886 // FIXME: pass in close point so we can check for closure
1887
1888 // given a segment, and a sense of where 'inside' is, return the next
1889 // segment. If this segment has an intersection, or ends in multiple
1890 // segments, find the mate that continues the outside.
1891 // note that if there are multiples, but no coincidence, we can limit
1892 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00001893
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001894 // mark found segments as done
1895
caryclark@google.com15fa1382012-05-07 20:49:36 +00001896 // start is the index of the beginning T of this edge
1897 // it is guaranteed to have an end which describes a non-zero length (?)
1898 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00001899 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001900 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
1901 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001902 const int startIndex = nextStart;
1903 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001904 int outerWinding = winding;
1905 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00001906 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001907 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1908 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00001909 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001910 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001911 outerWinding = innerWinding;
1912 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001913 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001914 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001915 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1916 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001917 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001918 #if PRECISE_T_SORT
1919 int end = nextExactSpan(startIndex, step);
1920 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001921 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001922 #endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001923 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001924 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001925 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001926 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001927 // mark the smaller of startIndex, endIndex done, and all adjacent
1928 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001929 #if DEBUG_WINDING
1930 SkDebugf("%s simple\n", __FUNCTION__);
1931 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001932 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001933 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001934 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00001935 double startT = other->fTs[nextStart].fT;
1936 nextEnd = nextStart;
1937 do {
1938 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001939 }
1940 #if PRECISE_T_SORT
1941 while (precisely_zero(startT - other->fTs[nextEnd].fT));
1942 #else
1943 while (approximately_zero(startT - other->fTs[nextEnd].fT));
1944 #endif
caryclark@google.com495f8e42012-05-31 13:13:11 +00001945 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001946 return other;
1947 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001948 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001949 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001950 SkASSERT(startIndex - endIndex != 0);
1951 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001952 addTwoAngles(startIndex, end, angles);
1953 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001954 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001955 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001956 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001957 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001958 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00001959 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001960 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001961 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001962 if (!sortable) {
1963 unsortable = true;
1964 return NULL;
1965 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001966 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001967 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001968 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00001969 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001970 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001971 int nextIndex = firstIndex + 1;
1972 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1973 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00001974 // FIXME: found done logic probably fails if there are more than 4
1975 // sorted angles. It should bias towards the first and last undone
1976 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001977 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00001978 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00001979 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001980 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00001981 bool altFlipped = false;
1982 bool foundFlipped = false;
1983 int foundMax = SK_MinS32;
1984 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001985 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00001986 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001987 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001988 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001989 nextIndex = 0;
1990 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001991 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00001992 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00001993 if (sumWinding) {
1994 lastNonZeroSum = sumWinding;
1995 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00001996 nextSegment = nextAngle->segment();
caryclark@google.com2ddff932012-08-07 21:25:27 +00001997 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00001998 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00001999 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002000 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002001 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2002 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002003 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002004 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002005 if (!active) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002006 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002007 // FIXME: seems like a bug that this isn't calling userInnerWinding
caryclark@google.com47580692012-07-23 12:14:49 +00002008 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002009 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002010 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002011 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002012 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002013 return NULL;
2014 }
caryclark@google.com47580692012-07-23 12:14:49 +00002015 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002016 foundAngle = nextAngle;
caryclark@google.com47580692012-07-23 12:14:49 +00002017 foundDone = nextSegment->done(*nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002018 foundFlipped = altFlipped;
2019 foundMax = maxWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002020 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002021 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002022 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002023 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002024 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002025 if (foundAngle && foundDone2) {
2026 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002027 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002028 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002029 foundAngle = nextAngle;
caryclark@google.com24bec792012-08-20 12:43:57 +00002030 foundDone2 = nextSegment->done(*nextAngle);
2031 foundFlipped = altFlipped;
2032 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002033 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002034 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002035 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002036 }
2037 // if the winding is non-zero, nextAngle does not connect to
2038 // current chain. If we haven't done so already, mark the angle
2039 // as done, record the winding value, and mark connected unambiguous
2040 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002041 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002042 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002043 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002044 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002045 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002046 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002047 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002048 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002049 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002050 }
2051 if (last) {
2052 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002053 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002054 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002055 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002056 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002057 if (!foundAngle) {
2058 return NULL;
2059 }
2060 nextStart = foundAngle->start();
2061 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002062 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002063 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002064 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2065 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002066 if (winding) {
2067 #if DEBUG_WINDING
2068 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2069 if (foundSum == SK_MinS32) {
2070 SkDebugf("?");
2071 } else {
2072 SkDebugf("%d", foundSum);
2073 }
2074 SkDebugf(" foundMax=");
2075 if (foundMax == SK_MinS32) {
2076 SkDebugf("?");
2077 } else {
2078 SkDebugf("%d", foundMax);
2079 }
2080 SkDebugf("\n");
2081 #endif
2082 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002083 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002084 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002085 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002086 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002087 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002088 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002089
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002090 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002091 const int startIndex = nextStart;
2092 const int endIndex = nextEnd;
2093 SkASSERT(startIndex != endIndex);
2094 int count = fTs.count();
2095 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2096 : startIndex > 0);
2097 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002098 #if PRECISE_T_SORT
2099 int end = nextExactSpan(startIndex, step);
2100 #else
caryclark@google.com24bec792012-08-20 12:43:57 +00002101 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002102 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002103 SkASSERT(end >= 0);
2104 Span* endSpan = &fTs[end];
2105 Segment* other;
2106 markDone(SkMin32(startIndex, endIndex), 1);
2107 if (isSimple(end)) {
2108 #if DEBUG_WINDING
2109 SkDebugf("%s simple\n", __FUNCTION__);
2110 #endif
2111 other = endSpan->fOther;
2112 nextStart = endSpan->fOtherIndex;
2113 double startT = other->fTs[nextStart].fT;
2114 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002115 if ((approximately_less_than_zero(startT) && step < 0)
2116 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002117 step = -step;
2118 SkDEBUGCODE(firstLoop = false;)
2119 }
2120 do {
2121 nextEnd = nextStart;
2122 do {
2123 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002124 }
2125 #if PRECISE_T_SORT
2126 while (precisely_zero(startT - other->fTs[nextEnd].fT));
2127 #else
2128 while (approximately_zero(startT - other->fTs[nextEnd].fT));
2129 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002130 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2131 break;
2132 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002133 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002134 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002135 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002136 SkDEBUGCODE(firstLoop = false;)
2137 step = -step;
2138 } while (true);
2139 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2140 return other;
2141 }
2142 SkTDArray<Angle> angles;
2143 SkASSERT(startIndex - endIndex != 0);
2144 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2145 addTwoAngles(startIndex, end, angles);
2146 buildAngles(end, angles);
2147 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002148 bool sortable = SortAngles(angles, sorted);
caryclark@google.com24bec792012-08-20 12:43:57 +00002149 int angleCount = angles.count();
2150 int firstIndex = findStartingEdge(sorted, startIndex, end);
2151 SkASSERT(firstIndex >= 0);
2152 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002153 debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002154 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002155 if (!sortable) {
2156 unsortable = true;
2157 return NULL;
2158 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002159 SkASSERT(sorted[firstIndex]->segment() == this);
2160 int nextIndex = firstIndex + 1;
2161 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2162 const Angle* nextAngle;
2163 Segment* nextSegment;
2164 do {
2165 if (nextIndex == angleCount) {
2166 nextIndex = 0;
2167 }
2168 nextAngle = sorted[nextIndex];
2169 nextSegment = nextAngle->segment();
2170 if (!nextSegment->done(*nextAngle)) {
2171 break;
2172 }
2173 if (++nextIndex == lastIndex) {
2174 return NULL;
2175 }
2176 } while (true);
2177 nextStart = nextAngle->start();
2178 nextEnd = nextAngle->end();
2179 return nextSegment;
2180 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002181
2182 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2183 int angleCount = sorted.count();
2184 int firstIndex = -1;
2185 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2186 const Angle* angle = sorted[angleIndex];
2187 if (angle->segment() == this && angle->start() == end &&
2188 angle->end() == start) {
2189 firstIndex = angleIndex;
2190 break;
2191 }
2192 }
2193 return firstIndex;
2194 }
2195
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002196 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com235f56a2012-09-14 14:19:30 +00002197 void findTooCloseToCall(bool isXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002198 int count = fTs.count();
2199 if (count < 3) { // require t=0, x, 1 at minimum
2200 return;
2201 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002202 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002203 int moCount;
2204 Span* match;
2205 Segment* mOther;
2206 do {
2207 match = &fTs[matchIndex];
2208 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002209 // FIXME: allow quads, cubics to be near coincident?
2210 if (mOther->fVerb == SkPath::kLine_Verb) {
2211 moCount = mOther->fTs.count();
2212 if (moCount >= 3) {
2213 break;
2214 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002215 }
2216 if (++matchIndex >= count) {
2217 return;
2218 }
2219 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002220 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002221 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002222 // look for a pair of nearby T values that map to the same (x,y) value
2223 // if found, see if the pair of other segments share a common point. If
2224 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002225 for (int index = matchIndex + 1; index < count; ++index) {
2226 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002227 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002228 continue;
2229 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002230 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002231 if (tOther->fVerb != SkPath::kLine_Verb) {
2232 continue; // FIXME: allow quads, cubics to be near coincident?
2233 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002234 int toCount = tOther->fTs.count();
2235 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002236 continue;
2237 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002238 const SkPoint* testPt = &xyAtT(test);
2239 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002240 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002241 moCount = toCount;
2242 match = test;
2243 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002244 matchPt = testPt;
2245 continue;
2246 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002247 int moStart = -1;
2248 int moEnd = -1;
2249 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002250 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002251 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002252 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002253 continue;
2254 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002255 if (moSpan.fOther == this) {
2256 if (moSpan.fOtherT == match->fT) {
2257 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002258 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002259 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002260 continue;
2261 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002262 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002263 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2264 moStart = -1;
2265 break;
2266 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002267 SkASSERT(moEnd == -1);
2268 moEnd = moIndex;
2269 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002270 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002271 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002272 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002273 continue;
2274 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002275 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002276 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002277 continue;
2278 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002279 int toStart = -1;
2280 int toEnd = -1;
2281 double toStartT, toEndT;
2282 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2283 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002284 if (toSpan.fDone) {
2285 continue;
2286 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002287 if (toSpan.fOther == this) {
2288 if (toSpan.fOtherT == test->fT) {
2289 toStart = toIndex;
2290 toStartT = toSpan.fT;
2291 }
2292 continue;
2293 }
2294 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002295 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2296 moStart = -1;
2297 break;
2298 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002299 SkASSERT(toEnd == -1);
2300 toEnd = toIndex;
2301 toEndT = toSpan.fT;
2302 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002303 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002304 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2305 if (toStart <= 0 || toEnd <= 0) {
2306 continue;
2307 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002308 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002309 continue;
2310 }
2311 // test to see if the segment between there and here is linear
2312 if (!mOther->isLinear(moStart, moEnd)
2313 || !tOther->isLinear(toStart, toEnd)) {
2314 continue;
2315 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002316 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002317 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002318 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002319 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002320 // FIXME: this is bogus for multiple ops
2321 // the xorMask needs to be accumulated from the union of the two
2322 // edges -- which means that the segment must have its own copy of the mask
2323 mOther->addTCoincident(isXor, moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002324 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002325 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002326 }
2327
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002328 // start here;
2329 // either:
2330 // a) mark spans with either end unsortable as done, or
2331 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2332 // when encountering an unsortable span
2333
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002334 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2335 // and use more concise logic like the old edge walker code?
2336 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002337 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002338 // iterate through T intersections and return topmost
2339 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002340 SkASSERT(!done());
2341 int firstT;
2342 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002343 SkPoint topPt;
2344 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002345 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002346 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002347 bool lastDone = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002348 bool lastUnsortableEnd;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002349 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002350 const Span& span = fTs[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002351 if ((!span.fDone && !span.fUnsortableStart) || (!lastDone && !lastUnsortableEnd)) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002352 const SkPoint& intercept = xyAtT(&span);
2353 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2354 && topPt.fX > intercept.fX)) {
2355 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002356 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002357 } else if (topPt == intercept) {
2358 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002359 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002360 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002361 lastDone = span.fDone;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002362 lastUnsortableEnd = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002363 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002364 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002365 int step = 1;
2366 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002367 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002368 step = -1;
2369 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002370 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002371 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002372 // if the topmost T is not on end, or is three-way or more, find left
2373 // look for left-ness from tLeft to firstT (matching y of other)
2374 SkTDArray<Angle> angles;
2375 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002376 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002377 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002378 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002379 (void) SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002380 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00002381 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002382 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002383 // skip edges that have already been processed
2384 firstT = -1;
2385 Segment* leftSegment;
2386 do {
2387 const Angle* angle = sorted[++firstT];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002388 if (angle->unsortable()) {
2389 // FIXME: if all angles are unsortable, find next topmost
2390 SkASSERT(firstT < angles.count() - 1);
2391 continue;
2392 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002393 leftSegment = angle->segment();
2394 tIndex = angle->end();
2395 endIndex = angle->start();
2396 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002397 return leftSegment;
2398 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002399
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002400 // FIXME: not crazy about this
2401 // when the intersections are performed, the other index is into an
2402 // incomplete array. as the array grows, the indices become incorrect
2403 // while the following fixes the indices up again, it isn't smart about
2404 // skipping segments whose indices are already correct
2405 // assuming we leave the code that wrote the index in the first place
2406 void fixOtherTIndex() {
2407 int iCount = fTs.count();
2408 for (int i = 0; i < iCount; ++i) {
2409 Span& iSpan = fTs[i];
2410 double oT = iSpan.fOtherT;
2411 Segment* other = iSpan.fOther;
2412 int oCount = other->fTs.count();
2413 for (int o = 0; o < oCount; ++o) {
2414 Span& oSpan = other->fTs[o];
2415 if (oT == oSpan.fT && this == oSpan.fOther) {
2416 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002417 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002418 }
2419 }
2420 }
2421 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002422
caryclark@google.com495f8e42012-05-31 13:13:11 +00002423 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002424 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002425 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002426 SkASSERT(end >= 0);
2427 if (multipleSpans(end)) {
2428 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002429 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002430 const Span& endSpan = fTs[end];
2431 Segment* other = endSpan.fOther;
2432 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002433 int otherEnd = other->nextSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002434 Span* last = other->innerChaseDone(index, step, winding);
2435 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002436 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002437 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002438
caryclark@google.com59823f72012-08-09 18:17:47 +00002439 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002440 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002441 SkASSERT(end >= 0);
2442 if (multipleSpans(end)) {
2443 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002444 }
2445 const Span& endSpan = fTs[end];
2446 Segment* other = endSpan.fOther;
2447 index = endSpan.fOtherIndex;
2448 int otherEnd = other->nextSpan(index, step);
2449 int min = SkMin32(index, otherEnd);
2450 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002451 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002452 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002453 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002454 Span* last = other->innerChaseWinding(index, step, winding);
2455 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002456 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002457 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002458
caryclark@google.com235f56a2012-09-14 14:19:30 +00002459 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2460 fDoneSpans = 0;
2461 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002462 fPts = pts;
2463 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002464 }
2465
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002466 bool intersected() const {
2467 return fTs.count() > 0;
2468 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002469
2470 bool isConnected(int startIndex, int endIndex) const {
2471 return fTs[startIndex].fWindSum != SK_MinS32
2472 || fTs[endIndex].fWindSum != SK_MinS32;
2473 }
2474
caryclark@google.com235f56a2012-09-14 14:19:30 +00002475 bool isHorizontal() const {
2476 return fBounds.fTop == fBounds.fBottom;
2477 }
2478
caryclark@google.com15fa1382012-05-07 20:49:36 +00002479 bool isLinear(int start, int end) const {
2480 if (fVerb == SkPath::kLine_Verb) {
2481 return true;
2482 }
2483 if (fVerb == SkPath::kQuad_Verb) {
2484 SkPoint qPart[3];
2485 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2486 return QuadIsLinear(qPart);
2487 } else {
2488 SkASSERT(fVerb == SkPath::kCubic_Verb);
2489 SkPoint cPart[4];
2490 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2491 return CubicIsLinear(cPart);
2492 }
2493 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002494
2495 // OPTIMIZE: successive calls could start were the last leaves off
2496 // or calls could specialize to walk forwards or backwards
2497 bool isMissing(double startT) const {
2498 size_t tCount = fTs.count();
2499 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002500 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002501 return false;
2502 }
2503 }
2504 return true;
2505 }
2506
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002507 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002508 int count = fTs.count();
2509 if (count == 2) {
2510 return true;
2511 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002512 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002513 if (approximately_less_than_zero(t)) {
2514 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002515 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002516 if (approximately_greater_than_one(t)) {
2517 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002518 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002519 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002520 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002521
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002522 bool isVertical() const {
2523 return fBounds.fLeft == fBounds.fRight;
2524 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002525
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002526 SkScalar leftMost(int start, int end) const {
2527 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2528 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002529
caryclark@google.com495f8e42012-05-31 13:13:11 +00002530 // this span is excluded by the winding rule -- chase the ends
2531 // as long as they are unambiguous to mark connections as done
2532 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002533 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002534 int index = angle->start();
2535 int endIndex = angle->end();
2536 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002537 Span* last = innerChaseDone(index, step, winding);
2538 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002539 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002540 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002541
caryclark@google.com59823f72012-08-09 18:17:47 +00002542 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002543 int index = angle->start();
2544 int endIndex = angle->end();
2545 int min = SkMin32(index, endIndex);
2546 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002547 Span* last = innerChaseWinding(index, step, winding);
2548 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002549 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002550 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002551
caryclark@google.com495f8e42012-05-31 13:13:11 +00002552 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002553 // This may be called when the segment is already marked done. While this
2554 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002555 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002556 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002557 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002558 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002559 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002560 double referenceT = fTs[index].fT;
2561 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002562 #if PRECISE_T_SORT
2563 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2564 markOneDone(__FUNCTION__, lesser, winding);
2565 }
2566 do {
2567 markOneDone(__FUNCTION__, index, winding);
2568 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2569 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002570 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002571 markOneDone(__FUNCTION__, lesser, winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002572 }
2573 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002574 markOneDone(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002575 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00002576 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002577 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002578
caryclark@google.com24bec792012-08-20 12:43:57 +00002579 void markOneDone(const char* funName, int tIndex, int winding) {
2580 Span* span = markOneWinding(funName, tIndex, winding);
2581 if (!span) {
2582 return;
2583 }
2584 span->fDone = true;
2585 fDoneSpans++;
2586 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002587
caryclark@google.com24bec792012-08-20 12:43:57 +00002588 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2589 Span& span = fTs[tIndex];
2590 if (span.fDone) {
2591 return NULL;
2592 }
2593 #if DEBUG_MARK_DONE
2594 debugShowNewWinding(funName, span, winding);
2595 #endif
2596 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002597 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002598 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002599 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002600 span.fWindSum = winding;
2601 return &span;
2602 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002603
caryclark@google.com59823f72012-08-09 18:17:47 +00002604 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002605 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002606 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002607 double referenceT = fTs[index].fT;
2608 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002609 #if PRECISE_T_SORT
2610 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2611 markOneWinding(__FUNCTION__, lesser, winding);
2612 }
2613 do {
2614 markOneWinding(__FUNCTION__, index, winding);
2615 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2616 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002617 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002618 markOneWinding(__FUNCTION__, lesser, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002619 }
2620 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002621 markOneWinding(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002622 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00002623 #endif
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002624 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002625
caryclark@google.com2ddff932012-08-07 21:25:27 +00002626 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002627 int nextDoorWind = SK_MaxS32;
2628 if (tIndex > 0) {
2629 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002630 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002631 nextDoorWind = below.fWindValue;
2632 }
2633 }
2634 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2635 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002636 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002637 nextDoorWind = above.fWindValue;
2638 }
2639 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002640 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2641 const Span& below = fTs[tIndex - 1];
2642 nextDoorWind = below.fWindValue;
2643 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002644 if (nextDoorWind != SK_MaxS32) {
2645 Span& newSpan = fTs[tIndex];
2646 newSpan.fWindValue = nextDoorWind;
2647 if (!nextDoorWind) {
2648 newSpan.fDone = true;
2649 ++fDoneSpans;
2650 }
2651 }
2652 }
2653
caryclark@google.com9764cc62012-07-12 19:29:45 +00002654 // return span if when chasing, two or more radiating spans are not done
2655 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2656 // candidate and the remaining spans have windValue == 0 (canceled by
2657 // coincidence). The coincident edges could either be removed altogether,
2658 // or this code could be more complicated in detecting this case. Worth it?
2659 bool multipleSpans(int end) const {
2660 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002661 }
2662
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002663 // This has callers for two different situations: one establishes the end
2664 // of the current span, and one establishes the beginning of the next span
2665 // (thus the name). When this is looking for the end of the current span,
2666 // coincidence is found when the beginning Ts contain -step and the end
2667 // contains step. When it is looking for the beginning of the next, the
2668 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002669 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002670 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002671 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002672 int count = fTs.count();
2673 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002674 while (step > 0 ? ++to < count : --to >= 0) {
2675 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002676 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002677 continue;
2678 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002679 return to;
2680 }
2681 return -1;
2682 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00002683
caryclark@google.coma461ff02012-10-11 12:54:23 +00002684#if PRECISE_T_SORT
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002685 // FIXME
2686 // this returns at any difference in T, vs. a preset minimum. It may be
2687 // that all callers to nextSpan should use this instead.
2688 int nextExactSpan(int from, int step) const {
2689 const Span& fromSpan = fTs[from];
2690 int count = fTs.count();
2691 int to = from;
2692 while (step > 0 ? ++to < count : --to >= 0) {
2693 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00002694 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002695 continue;
2696 }
2697 return to;
2698 }
2699 return -1;
2700 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002701#endif
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002702
caryclark@google.com235f56a2012-09-14 14:19:30 +00002703 bool operand() const {
2704 return fOperand;
2705 }
2706
2707 int oppSign(int startIndex, int endIndex) const {
2708 int result = startIndex < endIndex ? -fTs[startIndex].fWindValueOpp :
2709 fTs[endIndex].fWindValueOpp;
2710#if DEBUG_WIND_BUMP
2711 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2712#endif
2713 return result;
2714 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002715
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002716 const SkPoint* pts() const {
2717 return fPts;
2718 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002719
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002720 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002721 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002722 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2723 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002724 }
2725
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002726 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
2727 int angleCount = angles.count();
2728 int angleIndex;
2729 angleList.setReserve(angleCount);
2730 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2731 *angleList.append() = &angles[angleIndex];
2732 }
2733 QSort<Angle>(angleList.begin(), angleList.end() - 1);
2734 bool result = true;
2735 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2736 Angle& angle = angles[angleIndex];
2737 if (angle.unsortable()) {
2738 // so that it is available for early exclusion in findTop and others
2739 const SkTDArray<Span>* spans = angle.spans();
2740 Span* span = const_cast<Span*>(&(*spans)[angle.start()]);
2741 if (angle.start() < angle.end()) {
2742 span->fUnsortableStart = true;
2743 } else {
2744 --span;
2745 span->fUnsortableEnd = true;
2746 }
2747 result = false;
2748 }
2749 }
2750 return result;
2751 }
2752
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002753 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002754 const Span& span(int tIndex) const {
2755 return fTs[tIndex];
2756 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002757
caryclark@google.com235f56a2012-09-14 14:19:30 +00002758 int spanSign(const Angle* angle) const {
2759 SkASSERT(angle->segment() == this);
2760 return spanSign(angle->start(), angle->end());
2761 }
2762
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002763 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002764 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue :
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002765 fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002766#if DEBUG_WIND_BUMP
2767 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2768#endif
2769 return result;
2770 }
2771
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002772 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002773 double t(int tIndex) const {
2774 return fTs[tIndex].fT;
2775 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002776
caryclark@google.com18063442012-07-25 12:05:18 +00002777 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
2778 double start) {
2779 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002780 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00002781 *outsideTs.append() = end;
2782 *outsideTs.append() = start;
2783 }
2784 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002785
caryclark@google.com24bec792012-08-20 12:43:57 +00002786 void undoneSpan(int& start, int& end) {
2787 size_t tCount = fTs.count();
2788 size_t index;
2789 for (index = 0; index < tCount; ++index) {
2790 if (!fTs[index].fDone) {
2791 break;
2792 }
2793 }
2794 SkASSERT(index < tCount - 1);
2795 start = index;
2796 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002797 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00002798 SkASSERT(index < tCount);
2799 SkASSERT(index < tCount);
2800 end = index;
2801 }
caryclark@google.com18063442012-07-25 12:05:18 +00002802
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002803 void updatePts(const SkPoint pts[]) {
2804 fPts = pts;
2805 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002806
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002807 SkPath::Verb verb() const {
2808 return fVerb;
2809 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002810
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002811 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002812 return fTs[tIndex].fWindSum;
2813 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002814
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002815 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002816 int start = angle->start();
2817 int end = angle->end();
2818 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002819 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002820 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002821
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002822 int windValue(int tIndex) const {
2823 return fTs[tIndex].fWindValue;
2824 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002825
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002826 int windValue(const Angle* angle) const {
2827 int start = angle->start();
2828 int end = angle->end();
2829 int index = SkMin32(start, end);
2830 return windValue(index);
2831 }
2832
2833 SkScalar xAtT(const Span* span) const {
2834 return xyAtT(span).fX;
2835 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002836
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002837 const SkPoint& xyAtT(int index) const {
2838 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002839 }
2840
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002841 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002842 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002843 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002844 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002845 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002846 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002847 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002848 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002849 }
2850 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002851 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002852 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002853
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002854 SkScalar yAtT(int index) const {
2855 return yAtT(&fTs[index]);
2856 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002857
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002858 SkScalar yAtT(const Span* span) const {
2859 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002860 }
2861
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002862#if DEBUG_DUMP
2863 void dump() const {
2864 const char className[] = "Segment";
2865 const int tab = 4;
2866 for (int i = 0; i < fTs.count(); ++i) {
2867 SkPoint out;
2868 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
2869 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002870 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002871 tab + sizeof(className), className, fID,
2872 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002873 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002874 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002875 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002876 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00002877 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002878 }
2879#endif
2880
caryclark@google.com47580692012-07-23 12:14:49 +00002881#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00002882 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002883 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00002884 for (int i = 0; i < fTs.count(); ++i) {
2885 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
2886 return;
2887 }
2888 }
2889 SkASSERT(0);
2890 }
2891#endif
2892
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002893#if DEBUG_DUMP
2894 int debugID() const {
2895 return fID;
2896 }
2897#endif
2898
caryclark@google.com24bec792012-08-20 12:43:57 +00002899#if DEBUG_WINDING
2900 void debugShowSums() const {
2901 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
2902 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
2903 for (int i = 0; i < fTs.count(); ++i) {
2904 const Span& span = fTs[i];
2905 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
2906 if (span.fWindSum == SK_MinS32) {
2907 SkDebugf("?");
2908 } else {
2909 SkDebugf("%d", span.fWindSum);
2910 }
2911 SkDebugf("]");
2912 }
2913 SkDebugf("\n");
2914 }
2915#endif
2916
caryclark@google.comcc905052012-07-25 20:59:42 +00002917#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002918 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00002919 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00002920 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00002921 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00002922 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
2923 }
2924 SkDebugf("\n");
2925 }
2926#endif
2927
caryclark@google.com027de222012-07-12 12:52:50 +00002928#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002929 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00002930 if (done()) {
2931 return;
2932 }
2933 for (int i = 0; i < fTs.count(); ++i) {
2934 if (fTs[i].fDone) {
2935 continue;
2936 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002937 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00002938 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2939 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2940 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2941 }
2942 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00002943 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00002944 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00002945 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002946 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
2947 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
2948 if (fTs[i].fWindSum == SK_MinS32) {
2949 SkDebugf("?");
2950 } else {
2951 SkDebugf("%d", fTs[i].fWindSum);
2952 }
2953 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00002954 }
2955 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002956
2957 // This isn't useful yet -- but leaving it in for now in case i think of something
2958 // to use it for
2959 void validateActiveSpans() const {
2960 if (done()) {
2961 return;
2962 }
2963 int tCount = fTs.count();
2964 for (int index = 0; index < tCount; ++index) {
2965 if (fTs[index].fDone) {
2966 continue;
2967 }
2968 // count number of connections which are not done
2969 int first = index;
2970 double baseT = fTs[index].fT;
2971 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
2972 --first;
2973 }
2974 int last = index;
2975 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
2976 ++last;
2977 }
2978 int connections = 0;
2979 connections += first > 0 && !fTs[first - 1].fDone;
2980 for (int test = first; test <= last; ++test) {
2981 connections += !fTs[test].fDone;
2982 const Segment* other = fTs[test].fOther;
2983 int oIndex = fTs[test].fOtherIndex;
2984 connections += !other->fTs[oIndex].fDone;
2985 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
2986 }
2987 // SkASSERT(!(connections & 1));
2988 }
2989 }
caryclark@google.com027de222012-07-12 12:52:50 +00002990#endif
2991
caryclark@google.com0c803d02012-08-06 11:15:47 +00002992#if DEBUG_MARK_DONE
2993 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
2994 const SkPoint& pt = xyAtT(&span);
2995 SkDebugf("%s id=%d", fun, fID);
2996 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2997 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2998 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2999 }
3000 SkDebugf(") t=%1.9g (%1.9g,%1.9g) newWindSum=%d windSum=",
3001 span.fT, pt.fX, pt.fY, winding);
3002 if (span.fWindSum == SK_MinS32) {
3003 SkDebugf("?");
3004 } else {
3005 SkDebugf("%d", span.fWindSum);
3006 }
3007 SkDebugf(" windValue=%d\n", span.fWindValue);
3008 }
3009#endif
3010
caryclark@google.com47580692012-07-23 12:14:49 +00003011#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003012 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003013 const int contourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003014 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003015 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003016 int lastSum = contourWinding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003017 int windSum = lastSum - spanSign(angles[first]);
caryclark@google.com03f97062012-08-21 13:13:52 +00003018 SkDebugf("%s %s contourWinding=%d sign=%d\n", fun, __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +00003019 contourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003020 int index = first;
3021 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003022 do {
3023 const Angle& angle = *angles[index];
3024 const Segment& segment = *angle.segment();
3025 int start = angle.start();
3026 int end = angle.end();
3027 const Span& sSpan = segment.fTs[start];
3028 const Span& eSpan = segment.fTs[end];
3029 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003030 if (!firstTime) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003031 lastSum = windSum;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003032 windSum -= segment.spanSign(&angle);
caryclark@google.comafe56de2012-07-24 18:11:03 +00003033 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003034 SkDebugf("%s [%d] %s id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
caryclark@google.comc899ad92012-08-23 15:24:42 +00003035 " sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003036 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE ***" : "",
3037 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comc899ad92012-08-23 15:24:42 +00003038 start, segment.xAtT(&sSpan),
3039 segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
3040 segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
3041 lastSum, windSum, useInnerWinding(lastSum, windSum)
3042 ? windSum : lastSum, mSpan.fDone);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003043#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003044 angle.debugShow(segment.xyAtT(&sSpan));
3045#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003046 ++index;
3047 if (index == angles.count()) {
3048 index = 0;
3049 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003050 if (firstTime) {
3051 firstTime = false;
3052 }
caryclark@google.com47580692012-07-23 12:14:49 +00003053 } while (index != first);
3054 }
3055#endif
3056
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003057#if DEBUG_WINDING
3058 bool debugVerifyWinding(int start, int end, int winding) const {
3059 const Span& span = fTs[SkMin32(start, end)];
3060 int spanWinding = span.fWindSum;
3061 if (spanWinding == SK_MinS32) {
3062 return true;
3063 }
3064 int spanSign = SkSign32(start - end);
3065 int signedVal = spanSign * span.fWindValue;
3066 if (signedVal < 0) {
3067 spanWinding -= signedVal;
3068 }
3069 return span.fWindSum == winding;
3070 }
3071#endif
3072
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003073private:
3074 const SkPoint* fPts;
3075 SkPath::Verb fVerb;
3076 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003077 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00003078 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00003079 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003080#if DEBUG_DUMP
3081 int fID;
3082#endif
3083};
3084
caryclark@google.comb9738012012-07-03 19:53:30 +00003085class Contour;
3086
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003087struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003088 Contour* fContours[2];
3089 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003090 double fTs[2][2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00003091 bool fXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003092};
3093
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003094class Contour {
3095public:
3096 Contour() {
3097 reset();
3098#if DEBUG_DUMP
3099 fID = ++gContourID;
3100#endif
3101 }
3102
3103 bool operator<(const Contour& rh) const {
3104 return fBounds.fTop == rh.fBounds.fTop
3105 ? fBounds.fLeft < rh.fBounds.fLeft
3106 : fBounds.fTop < rh.fBounds.fTop;
3107 }
3108
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003109 void addCoincident(int index, Contour* other, int otherIndex,
3110 const Intersections& ts, bool swap) {
3111 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00003112 coincidence.fContours[0] = this;
3113 coincidence.fContours[1] = other;
3114 coincidence.fSegments[0] = index;
3115 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003116 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3117 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3118 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3119 coincidence.fTs[swap][0] = ts.fT[0][0];
3120 coincidence.fTs[swap][1] = ts.fT[0][1];
3121 coincidence.fTs[!swap][0] = ts.fT[1][0];
3122 coincidence.fTs[!swap][1] = ts.fT[1][1];
3123 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3124 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3125 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3126 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3127 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3128 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3129 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003130 coincidence.fXor = fOperand == other->fOperand ? fXor : true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003131 }
3132
3133 void addCross(const Contour* crosser) {
3134#ifdef DEBUG_CROSS
3135 for (int index = 0; index < fCrosses.count(); ++index) {
3136 SkASSERT(fCrosses[index] != crosser);
3137 }
3138#endif
3139 *fCrosses.append() = crosser;
3140 }
3141
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003142 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003143 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003144 fContainsCurves = true;
3145 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003146
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003147 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003148 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003149 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003150 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003151
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003152 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3153 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3154 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003155
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003156 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003157 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003158 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003159 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003160 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003161
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003162 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3163 containsIntercepts();
3164 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3165 }
3166
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003167 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003168 return fBounds;
3169 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003170
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003171 void collapseTriangles() {
3172 int segmentCount = fSegments.count();
3173 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3174 fSegments[sIndex].collapseTriangles(fXor);
3175 }
3176 }
3177
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003178 void complete() {
3179 setBounds();
3180 fContainsIntercepts = false;
3181 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003182
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003183 void containsIntercepts() {
3184 fContainsIntercepts = true;
3185 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003186
rmistry@google.comd6176b02012-08-23 18:14:13 +00003187 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003188 int &tIndex, double& hitT) {
3189 int segmentCount = fSegments.count();
3190 const Segment* bestSegment = NULL;
3191 for (int test = 0; test < segmentCount; ++test) {
3192 Segment* testSegment = &fSegments[test];
3193 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003194 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003195 continue;
3196 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003197 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003198 continue;
3199 }
3200 if (bounds.fLeft > basePt.fX) {
3201 continue;
3202 }
3203 if (bounds.fRight < basePt.fX) {
3204 continue;
3205 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003206 if (bounds.fLeft == bounds.fRight) {
3207 continue;
3208 }
3209 #if 0
3210 bool leftHalf = bounds.fLeft == basePt.fX;
3211 bool rightHalf = bounds.fRight == basePt.fX;
3212 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3213 basePt, leftHalf, rightHalf)) {
3214 continue;
3215 }
3216 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003217 double testHitT;
3218 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3219 if (testT >= 0) {
3220 bestSegment = testSegment;
3221 tIndex = testT;
3222 hitT = testHitT;
3223 }
3224 }
3225 return bestSegment;
3226 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003227
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003228 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003229 for (int index = 0; index < fCrosses.count(); ++index) {
3230 if (fCrosses[index] == crosser) {
3231 return true;
3232 }
3233 }
3234 return false;
3235 }
3236
caryclark@google.com235f56a2012-09-14 14:19:30 +00003237 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003238 int segmentCount = fSegments.count();
3239 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003240 fSegments[sIndex].findTooCloseToCall(fXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003241 }
3242 }
3243
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003244 void fixOtherTIndex() {
3245 int segmentCount = fSegments.count();
3246 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3247 fSegments[sIndex].fixOtherTIndex();
3248 }
3249 }
3250
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003251 void reset() {
3252 fSegments.reset();
3253 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003254 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003255 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003256
caryclark@google.com235f56a2012-09-14 14:19:30 +00003257 // FIXME: for binary ops, need to keep both ops winding contributions separately
3258 // in edge array
3259 void resolveCoincidence() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003260 int count = fCoincidences.count();
3261 for (int index = 0; index < count; ++index) {
3262 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003263 Contour* thisContour = coincidence.fContours[0];
3264 Contour* otherContour = coincidence.fContours[1];
3265 int thisIndex = coincidence.fSegments[0];
3266 int otherIndex = coincidence.fSegments[1];
3267 Segment& thisOne = thisContour->fSegments[thisIndex];
3268 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003269 #if DEBUG_CONCIDENT
3270 thisOne.debugShowTs();
3271 other.debugShowTs();
3272 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003273 double startT = coincidence.fTs[0][0];
3274 double endT = coincidence.fTs[0][1];
caryclark@google.com32546db2012-08-31 20:55:07 +00003275 bool opposite = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003276 if (startT > endT) {
3277 SkTSwap<double>(startT, endT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003278 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003279 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003280 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003281 double oStartT = coincidence.fTs[1][0];
3282 double oEndT = coincidence.fTs[1][1];
3283 if (oStartT > oEndT) {
3284 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003285 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003286 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003287 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com32546db2012-08-31 20:55:07 +00003288 if (opposite) {
caryclark@google.comb9738012012-07-03 19:53:30 +00003289 // make sure startT and endT have t entries
caryclark@google.com32546db2012-08-31 20:55:07 +00003290 SkASSERT(opposite);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003291 if (startT > 0 || oEndT < 1
3292 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3293 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003294 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003295 if (oStartT > 0 || endT < 1
3296 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3297 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003298 }
3299 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003300 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003301 if (startT > 0 || oStartT > 0
3302 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003303 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003304 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003305 if (endT < 1 || oEndT < 1
3306 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003307 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003308 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003309 thisOne.addTCoincident(coincidence.fXor, startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003310 }
caryclark@google.com47580692012-07-23 12:14:49 +00003311 #if DEBUG_CONCIDENT
3312 thisOne.debugShowTs();
3313 other.debugShowTs();
3314 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003315 }
3316 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003317
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003318 const SkTArray<Segment>& segments() {
3319 return fSegments;
3320 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003321
caryclark@google.com235f56a2012-09-14 14:19:30 +00003322 void setOperand(bool isOp) {
3323 fOperand = isOp;
3324 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003325
caryclark@google.com235f56a2012-09-14 14:19:30 +00003326 void setXor(bool isXor) {
3327 fXor = isXor;
3328 }
3329
caryclark@google.com15fa1382012-05-07 20:49:36 +00003330 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3331 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003332 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003333 // segments' top, and not a true sort, so it could be ameniable to regular
3334 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003335 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003336 int segmentCount = fSegments.count();
3337 SkASSERT(segmentCount > 0);
3338 int best = -1;
3339 Segment* bestSegment = NULL;
3340 while (++best < segmentCount) {
3341 Segment* testSegment = &fSegments[best];
3342 if (testSegment->done()) {
3343 continue;
3344 }
3345 bestSegment = testSegment;
3346 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003347 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003348 if (!bestSegment) {
3349 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003350 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003351 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003352 for (int test = best + 1; test < segmentCount; ++test) {
3353 Segment* testSegment = &fSegments[test];
3354 if (testSegment->done()) {
3355 continue;
3356 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003357 if (testSegment->bounds().fTop > bestTop) {
3358 continue;
3359 }
3360 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003361 if (bestTop > testTop) {
3362 bestTop = testTop;
3363 bestSegment = testSegment;
3364 }
3365 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003366 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003367 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003368 }
3369
caryclark@google.com24bec792012-08-20 12:43:57 +00003370 Segment* undoneSegment(int& start, int& end) {
3371 int segmentCount = fSegments.count();
3372 for (int test = 0; test < segmentCount; ++test) {
3373 Segment* testSegment = &fSegments[test];
3374 if (testSegment->done()) {
3375 continue;
3376 }
3377 testSegment->undoneSpan(start, end);
3378 return testSegment;
3379 }
3380 return NULL;
3381 }
3382
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003383 int updateSegment(int index, const SkPoint* pts) {
3384 Segment& segment = fSegments[index];
3385 segment.updatePts(pts);
3386 return segment.verb() + 1;
3387 }
3388
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003389#if DEBUG_TEST
3390 SkTArray<Segment>& debugSegments() {
3391 return fSegments;
3392 }
3393#endif
3394
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003395#if DEBUG_DUMP
3396 void dump() {
3397 int i;
3398 const char className[] = "Contour";
3399 const int tab = 4;
3400 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
3401 for (i = 0; i < fSegments.count(); ++i) {
3402 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
3403 className, i);
3404 fSegments[i].dump();
3405 }
3406 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
3407 tab + sizeof(className), className,
3408 fBounds.fLeft, fBounds.fTop,
3409 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003410 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
3411 className, fContainsIntercepts);
3412 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
3413 className, fContainsCurves);
3414 }
3415#endif
3416
caryclark@google.com027de222012-07-12 12:52:50 +00003417#if DEBUG_ACTIVE_SPANS
3418 void debugShowActiveSpans() {
3419 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003420 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00003421 }
3422 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003423
3424 void validateActiveSpans() {
3425 for (int index = 0; index < fSegments.count(); ++index) {
3426 fSegments[index].validateActiveSpans();
3427 }
3428 }
caryclark@google.com027de222012-07-12 12:52:50 +00003429#endif
3430
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003431protected:
3432 void setBounds() {
3433 int count = fSegments.count();
3434 if (count == 0) {
3435 SkDebugf("%s empty contour\n", __FUNCTION__);
3436 SkASSERT(0);
3437 // FIXME: delete empty contour?
3438 return;
3439 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003440 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003441 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003442 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003443 }
3444 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003445
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003446private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003447 SkTArray<Segment> fSegments;
3448 SkTDArray<Coincidence> fCoincidences;
3449 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003450 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003451 bool fContainsIntercepts;
3452 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003453 bool fOperand; // true for the second argument to a binary operator
3454 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003455#if DEBUG_DUMP
3456 int fID;
3457#endif
3458};
3459
3460class EdgeBuilder {
3461public:
3462
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003463EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003464 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003465 , fContours(contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003466 , fCurrentContour(NULL)
3467 , fOperand(false)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003468{
caryclark@google.com235f56a2012-09-14 14:19:30 +00003469 fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003470#if DEBUG_DUMP
3471 gContourID = 0;
3472 gSegmentID = 0;
3473#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00003474 fSecondHalf = preFetch();
3475}
3476
3477void addOperand(const SkPath& path) {
3478 fPath = &path;
3479 fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
3480 preFetch();
3481}
3482
3483void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003484 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003485 complete();
3486 if (fCurrentContour && !fCurrentContour->segments().count()) {
3487 fContours.pop_back();
3488 }
3489 // correct pointers in contours since fReducePts may have moved as it grew
3490 int cIndex = 0;
3491 int extraCount = fExtra.count();
3492 SkASSERT(extraCount == 0 || fExtra[0] == -1);
3493 int eIndex = 0;
3494 int rIndex = 0;
3495 while (++eIndex < extraCount) {
3496 int offset = fExtra[eIndex];
3497 if (offset < 0) {
3498 ++cIndex;
3499 continue;
3500 }
3501 fCurrentContour = &fContours[cIndex];
3502 rIndex += fCurrentContour->updateSegment(offset - 1,
3503 &fReducePts[rIndex]);
3504 }
3505 fExtra.reset(); // we're done with this
3506}
3507
3508ShapeOpMask xorMask() const {
3509 return fXorMask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003510}
3511
3512protected:
3513
3514void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003515 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003516 fCurrentContour->complete();
3517 fCurrentContour = NULL;
3518 }
3519}
3520
caryclark@google.com235f56a2012-09-14 14:19:30 +00003521// FIXME:remove once we can access path pts directly
3522int preFetch() {
3523 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003524 SkPoint pts[4];
3525 SkPath::Verb verb;
3526 do {
3527 verb = iter.next(pts);
3528 *fPathVerbs.append() = verb;
3529 if (verb == SkPath::kMove_Verb) {
3530 *fPathPts.append() = pts[0];
3531 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
3532 fPathPts.append(verb, &pts[1]);
3533 }
3534 } while (verb != SkPath::kDone_Verb);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003535 return fPathVerbs.count();
3536}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003537
caryclark@google.com235f56a2012-09-14 14:19:30 +00003538void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003539 SkPath::Verb reducedVerb;
3540 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003541 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003542 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003543 const SkPoint* finalCurveStart = NULL;
3544 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003545 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003546 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
3547 switch (verb) {
3548 case SkPath::kMove_Verb:
3549 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003550 if (!fCurrentContour) {
3551 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003552 fCurrentContour->setOperand(fOperand);
3553 fCurrentContour->setXor(fXorMask == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003554 *fExtra.append() = -1; // start new contour
3555 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003556 finalCurveEnd = pointsPtr++;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003557 continue;
3558 case SkPath::kLine_Verb:
3559 // skip degenerate points
3560 if (pointsPtr[-1].fX != pointsPtr[0].fX
3561 || pointsPtr[-1].fY != pointsPtr[0].fY) {
3562 fCurrentContour->addLine(&pointsPtr[-1]);
3563 }
3564 break;
3565 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00003566
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003567 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
3568 if (reducedVerb == 0) {
3569 break; // skip degenerate points
3570 }
3571 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00003572 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003573 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003574 break;
3575 }
3576 fCurrentContour->addQuad(&pointsPtr[-1]);
3577 break;
3578 case SkPath::kCubic_Verb:
3579 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
3580 if (reducedVerb == 0) {
3581 break; // skip degenerate points
3582 }
3583 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003584 *fExtra.append() =
3585 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003586 break;
3587 }
3588 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003589 *fExtra.append() =
3590 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003591 break;
3592 }
3593 fCurrentContour->addCubic(&pointsPtr[-1]);
3594 break;
3595 case SkPath::kClose_Verb:
3596 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003597 if (finalCurveStart && finalCurveEnd
3598 && *finalCurveStart != *finalCurveEnd) {
3599 *fReducePts.append() = *finalCurveStart;
3600 *fReducePts.append() = *finalCurveEnd;
3601 *fExtra.append() =
3602 fCurrentContour->addLine(fReducePts.end() - 2);
3603 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003604 complete();
3605 continue;
3606 default:
3607 SkDEBUGFAIL("bad verb");
3608 return;
3609 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003610 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003611 pointsPtr += verb;
3612 SkASSERT(fCurrentContour);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003613 if (verbPtr == endOfFirstHalf) {
3614 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003615 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003616 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003617}
3618
3619private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00003620 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003621 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003622 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003623 Contour* fCurrentContour;
3624 SkTArray<Contour>& fContours;
3625 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003626 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com235f56a2012-09-14 14:19:30 +00003627 ShapeOpMask fXorMask;
3628 int fSecondHalf;
3629 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003630};
3631
3632class Work {
3633public:
3634 enum SegmentType {
3635 kHorizontalLine_Segment = -1,
3636 kVerticalLine_Segment = 0,
3637 kLine_Segment = SkPath::kLine_Verb,
3638 kQuad_Segment = SkPath::kQuad_Verb,
3639 kCubic_Segment = SkPath::kCubic_Verb,
3640 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00003641
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003642 void addCoincident(Work& other, const Intersections& ts, bool swap) {
3643 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
3644 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003645
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003646 // FIXME: does it make sense to write otherIndex now if we're going to
3647 // fix it up later?
3648 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003649 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003650 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003651
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003652 // Avoid collapsing t values that are close to the same since
3653 // we walk ts to describe consecutive intersections. Since a pair of ts can
3654 // be nearly equal, any problems caused by this should be taken care
3655 // of later.
3656 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003657 int addT(double newT, const Work& other) {
3658 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003659 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003660
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003661 bool advance() {
3662 return ++fIndex < fLast;
3663 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003664
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003665 SkScalar bottom() const {
3666 return bounds().fBottom;
3667 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003668
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003669 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003670 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003671 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003672
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003673 const SkPoint* cubic() const {
3674 return fCubic;
3675 }
3676
3677 void init(Contour* contour) {
3678 fContour = contour;
3679 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003680 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003681 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003682
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003683 bool isAdjacent(const Work& next) {
3684 return fContour == next.fContour && fIndex + 1 == next.fIndex;
3685 }
3686
3687 bool isFirstLast(const Work& next) {
3688 return fContour == next.fContour && fIndex == 0
3689 && next.fIndex == fLast - 1;
3690 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003691
3692 SkScalar left() const {
3693 return bounds().fLeft;
3694 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003695
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003696 void promoteToCubic() {
3697 fCubic[0] = pts()[0];
3698 fCubic[2] = pts()[1];
3699 fCubic[3] = pts()[2];
3700 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
3701 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
3702 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
3703 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
3704 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003705
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003706 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003707 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003708 }
3709
3710 SkScalar right() const {
3711 return bounds().fRight;
3712 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003713
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003714 ptrdiff_t segmentIndex() const {
3715 return fIndex;
3716 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003717
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003718 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003719 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003720 SegmentType type = (SegmentType) segment.verb();
3721 if (type != kLine_Segment) {
3722 return type;
3723 }
3724 if (segment.isHorizontal()) {
3725 return kHorizontalLine_Segment;
3726 }
3727 if (segment.isVertical()) {
3728 return kVerticalLine_Segment;
3729 }
3730 return kLine_Segment;
3731 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003732
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003733 bool startAfter(const Work& after) {
3734 fIndex = after.fIndex;
3735 return advance();
3736 }
3737
3738 SkScalar top() const {
3739 return bounds().fTop;
3740 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003741
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003742 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003743 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003744 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003745
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003746 SkScalar x() const {
3747 return bounds().fLeft;
3748 }
3749
3750 bool xFlipped() const {
3751 return x() != pts()[0].fX;
3752 }
3753
3754 SkScalar y() const {
3755 return bounds().fTop;
3756 }
3757
3758 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003759 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003760 }
3761
3762protected:
3763 Contour* fContour;
3764 SkPoint fCubic[4];
3765 int fIndex;
3766 int fLast;
3767};
3768
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003769#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003770static void debugShowLineIntersection(int pts, const Work& wt,
3771 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003772 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003773 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
3774 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
3775 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
3776 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003777 return;
3778 }
3779 SkPoint wtOutPt, wnOutPt;
3780 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3781 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00003782 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003783 __FUNCTION__,
3784 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3785 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
3786 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00003787 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003788 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00003789 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003790 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3791 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
3792 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00003793 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
3794 }
3795 SkDebugf("\n");
3796}
3797
3798static void debugShowQuadIntersection(int pts, const Work& wt,
3799 const Work& wn, const double wtTs[2], const double wnTs[2]) {
3800 if (!pts) {
3801 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
3802 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
3803 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00003804 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00003805 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
3806 wt.pts()[2].fX, wt.pts()[2].fY );
3807 return;
3808 }
3809 SkPoint wtOutPt, wnOutPt;
3810 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3811 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
3812 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
3813 __FUNCTION__,
3814 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3815 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
3816 wtOutPt.fX, wtOutPt.fY);
3817 if (pts == 2) {
3818 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
3819 }
3820 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
3821 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3822 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
3823 wnOutPt.fX, wnOutPt.fY);
3824 if (pts == 2) {
3825 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003826 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003827 SkDebugf("\n");
3828}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003829#else
3830static void debugShowLineIntersection(int , const Work& ,
3831 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003832}
caryclark@google.coma461ff02012-10-11 12:54:23 +00003833
3834static void debugShowQuadIntersection(int , const Work& ,
3835 const Work& , const double [2], const double [2]) {
3836}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003837#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003838
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003839static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003840
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003841 if (test != next) {
3842 if (test->bounds().fBottom < next->bounds().fTop) {
3843 return false;
3844 }
3845 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
3846 return true;
3847 }
3848 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003849 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003850 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003851 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003852 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003853 Work wn;
3854 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003855 if (test == next && !wn.startAfter(wt)) {
3856 continue;
3857 }
3858 do {
3859 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
3860 continue;
3861 }
3862 int pts;
3863 Intersections ts;
3864 bool swap = false;
3865 switch (wt.segmentType()) {
3866 case Work::kHorizontalLine_Segment:
3867 swap = true;
3868 switch (wn.segmentType()) {
3869 case Work::kHorizontalLine_Segment:
3870 case Work::kVerticalLine_Segment:
3871 case Work::kLine_Segment: {
3872 pts = HLineIntersect(wn.pts(), wt.left(),
3873 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003874 debugShowLineIntersection(pts, wt, wn,
3875 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003876 break;
3877 }
3878 case Work::kQuad_Segment: {
3879 pts = HQuadIntersect(wn.pts(), wt.left(),
3880 wt.right(), wt.y(), wt.xFlipped(), ts);
3881 break;
3882 }
3883 case Work::kCubic_Segment: {
3884 pts = HCubicIntersect(wn.pts(), wt.left(),
3885 wt.right(), wt.y(), wt.xFlipped(), ts);
3886 break;
3887 }
3888 default:
3889 SkASSERT(0);
3890 }
3891 break;
3892 case Work::kVerticalLine_Segment:
3893 swap = true;
3894 switch (wn.segmentType()) {
3895 case Work::kHorizontalLine_Segment:
3896 case Work::kVerticalLine_Segment:
3897 case Work::kLine_Segment: {
3898 pts = VLineIntersect(wn.pts(), wt.top(),
3899 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003900 debugShowLineIntersection(pts, wt, wn,
3901 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003902 break;
3903 }
3904 case Work::kQuad_Segment: {
3905 pts = VQuadIntersect(wn.pts(), wt.top(),
3906 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3907 break;
3908 }
3909 case Work::kCubic_Segment: {
3910 pts = VCubicIntersect(wn.pts(), wt.top(),
3911 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3912 break;
3913 }
3914 default:
3915 SkASSERT(0);
3916 }
3917 break;
3918 case Work::kLine_Segment:
3919 switch (wn.segmentType()) {
3920 case Work::kHorizontalLine_Segment:
3921 pts = HLineIntersect(wt.pts(), wn.left(),
3922 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003923 debugShowLineIntersection(pts, wt, wn,
3924 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003925 break;
3926 case Work::kVerticalLine_Segment:
3927 pts = VLineIntersect(wt.pts(), wn.top(),
3928 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003929 debugShowLineIntersection(pts, wt, wn,
3930 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003931 break;
3932 case Work::kLine_Segment: {
3933 pts = LineIntersect(wt.pts(), wn.pts(), ts);
3934 debugShowLineIntersection(pts, wt, wn,
3935 ts.fT[1], ts.fT[0]);
3936 break;
3937 }
3938 case Work::kQuad_Segment: {
3939 swap = true;
3940 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
3941 break;
3942 }
3943 case Work::kCubic_Segment: {
3944 swap = true;
3945 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
3946 break;
3947 }
3948 default:
3949 SkASSERT(0);
3950 }
3951 break;
3952 case Work::kQuad_Segment:
3953 switch (wn.segmentType()) {
3954 case Work::kHorizontalLine_Segment:
3955 pts = HQuadIntersect(wt.pts(), wn.left(),
3956 wn.right(), wn.y(), wn.xFlipped(), ts);
3957 break;
3958 case Work::kVerticalLine_Segment:
3959 pts = VQuadIntersect(wt.pts(), wn.top(),
3960 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3961 break;
3962 case Work::kLine_Segment: {
3963 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
3964 break;
3965 }
3966 case Work::kQuad_Segment: {
3967 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00003968 debugShowQuadIntersection(pts, wt, wn,
3969 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003970 break;
3971 }
3972 case Work::kCubic_Segment: {
3973 wt.promoteToCubic();
3974 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
3975 break;
3976 }
3977 default:
3978 SkASSERT(0);
3979 }
3980 break;
3981 case Work::kCubic_Segment:
3982 switch (wn.segmentType()) {
3983 case Work::kHorizontalLine_Segment:
3984 pts = HCubicIntersect(wt.pts(), wn.left(),
3985 wn.right(), wn.y(), wn.xFlipped(), ts);
3986 break;
3987 case Work::kVerticalLine_Segment:
3988 pts = VCubicIntersect(wt.pts(), wn.top(),
3989 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3990 break;
3991 case Work::kLine_Segment: {
3992 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
3993 break;
3994 }
3995 case Work::kQuad_Segment: {
3996 wn.promoteToCubic();
3997 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
3998 break;
3999 }
4000 case Work::kCubic_Segment: {
4001 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4002 break;
4003 }
4004 default:
4005 SkASSERT(0);
4006 }
4007 break;
4008 default:
4009 SkASSERT(0);
4010 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004011 if (!foundCommonContour && pts > 0) {
4012 test->addCross(next);
4013 next->addCross(test);
4014 foundCommonContour = true;
4015 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004016 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004017 if (pts == 2) {
4018 if (wn.segmentType() <= Work::kLine_Segment
4019 && wt.segmentType() <= Work::kLine_Segment) {
4020 wt.addCoincident(wn, ts, swap);
4021 continue;
4022 }
4023 if (wn.segmentType() == Work::kQuad_Segment
4024 && wt.segmentType() == Work::kQuad_Segment
4025 && ts.coincidentUsed() == 2) {
4026 wt.addCoincident(wn, ts, swap);
4027 continue;
4028 }
4029
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004030 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004031 int pt2 = 0;
4032 int pt2inc = 1;
4033 if (ts.fFlip) {
4034 pt2 = pts - 1;
4035 pt2inc = -1;
4036 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004037 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004038 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4039 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004040 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4041 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004042 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4043 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
4044 pt2 += pt2inc;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004045 }
4046 } while (wn.advance());
4047 } while (wt.advance());
4048 return true;
4049}
4050
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004051// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004052// see if coincidence is formed by clipping non-concident segments
caryclark@google.com235f56a2012-09-14 14:19:30 +00004053static void coincidenceCheck(SkTDArray<Contour*>& contourList) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004054 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004055 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004056 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004057 contour->resolveCoincidence();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004058 }
4059 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4060 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004061 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004062 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004063#if 0
4064 // OPTIMIZATION: this check could be folded in with findTooClose -- maybe
4065 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4066 Contour* contour = contourList[cIndex];
4067 contour->collapseTriangles();
4068 }
4069#endif
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004070}
4071
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004072// project a ray from the top of the contour up and see if it hits anything
4073// note: when we compute line intersections, we keep track of whether
4074// two contours touch, so we need only look at contours not touching this one.
4075// OPTIMIZATION: sort contourList vertically to avoid linear walk
4076static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004077 const Segment* current, int index, int endIndex) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004078 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004079 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004080 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00004081 const Segment* test = NULL;
4082 int tIndex;
4083 double tHit;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004084 // bool checkCrosses = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004085 for (int cTest = 0; cTest < contourCount; ++cTest) {
4086 Contour* contour = contourList[cTest];
4087 if (basePt.fY < contour->bounds().fTop) {
4088 continue;
4089 }
4090 if (bestY > contour->bounds().fBottom) {
4091 continue;
4092 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004093#if 0
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004094 // even though the contours crossed, if spans cancel through concidence,
4095 // the contours may be not have any span links to chase, and the current
4096 // segment may be isolated. Detect this by seeing if current has
4097 // uninitialized wind sums. If so, project a ray instead of relying on
4098 // previously found intersections.
4099 if (baseContour == contour) {
4100 continue;
4101 }
4102 if (checkCrosses && baseContour->crosses(contour)) {
4103 if (current->isConnected(index, endIndex)) {
4104 continue;
4105 }
4106 checkCrosses = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004107 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004108#endif
caryclark@google.com47580692012-07-23 12:14:49 +00004109 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
4110 if (next) {
4111 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004112 }
caryclark@google.com47580692012-07-23 12:14:49 +00004113 }
4114 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00004115 return 0;
4116 }
4117 int winding, windValue;
4118 // If the ray hit the end of a span, we need to construct the wheel of
4119 // angles to find the span closest to the ray -- even if there are just
4120 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004121 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004122 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00004123 SkTDArray<Angle> angles;
4124 int end = test->nextSpan(tIndex, 1);
4125 if (end < 0) {
4126 end = test->nextSpan(tIndex, -1);
4127 }
4128 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00004129 SkASSERT(angles.count() > 0);
4130 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
4131#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00004132 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00004133#endif
4134 return 0;
4135 }
caryclark@google.com47580692012-07-23 12:14:49 +00004136 test->buildAngles(tIndex, angles);
4137 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00004138 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00004139 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004140 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00004141 // hour after 6 o'clock
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004142 (void) Segment::SortAngles(angles, sorted);
caryclark@google.com47580692012-07-23 12:14:49 +00004143#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004144 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00004145#endif
4146 // walk the sorted angle fan to find the lowest angle
4147 // above the base point. Currently, the first angle in the sorted array
4148 // is 12 noon or an earlier hour (the next counterclockwise)
4149 int count = sorted.count();
4150 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004151 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00004152 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004153 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00004154 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00004155 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004156 if (angle->unsortable()) {
4157 continue;
4158 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004159 if (baseMatches && angle->isHorizontal()) {
4160 continue;
4161 }
4162 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00004163 test = angle->segment();
4164 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
4165 const SkPoint* pts = test->pts();
4166 indexDx = pts[2].fX - pts[1].fX - indexDx;
4167 }
caryclark@google.com47580692012-07-23 12:14:49 +00004168 if (indexDx < 0) {
4169 left = index;
4170 } else if (indexDx > 0) {
4171 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00004172 int previous = index - 1;
4173 if (previous < 0) {
4174 previous = count - 1;
4175 }
4176 const Angle* prev = sorted[previous];
4177 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
4178#if DEBUG_SORT
4179 SkDebugf("%s use prev\n", __FUNCTION__);
4180#endif
4181 right = previous;
4182 }
caryclark@google.com47580692012-07-23 12:14:49 +00004183 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004184 } else {
4185 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004186 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004187 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004188 if (left < 0 && right < 0) {
4189 left = mid;
4190 }
caryclark@google.com47580692012-07-23 12:14:49 +00004191 SkASSERT(left >= 0 || right >= 0);
4192 if (left < 0) {
4193 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004194 } else if (left >= 0 && mid >= 0 && right >= 0
4195 && sorted[mid]->sign() == sorted[right]->sign()) {
4196 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004197 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004198 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004199 test = angle->segment();
4200 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004201 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004202 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004203#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004204 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4205 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004206#endif
4207 } else {
4208 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004209 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004210 windValue = test->windValue(tIndex);
4211#if DEBUG_WINDING
4212 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4213 windValue);
4214#endif
4215 }
4216 // see if a + change in T results in a +/- change in X (compute x'(T))
4217 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004218 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4219 const SkPoint* pts = test->pts();
4220 dx = pts[2].fX - pts[1].fX - dx;
4221 }
caryclark@google.com47580692012-07-23 12:14:49 +00004222#if DEBUG_WINDING
4223 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4224#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004225 SkASSERT(dx != 0);
4226 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004227 winding += dx > 0 ? -windValue : windValue;
4228#if DEBUG_WINDING
4229 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4230#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004231 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004232 return winding;
4233}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004234
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004235// OPTIMIZATION: not crazy about linear search here to find top active y.
4236// seems like we should break down and do the sort, or maybe sort each
rmistry@google.comd6176b02012-08-23 18:14:13 +00004237// contours' segments?
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004238// Once the segment array is built, there's no reason I can think of not to
4239// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004240// FIXME: return the contour found to pass to inner contour check
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004241static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004242 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004243 int cIndex = 0;
4244 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004245 SkScalar bestY = SK_ScalarMax;
4246 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004247 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004248 contour = contourList[cIndex];
4249 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004250 } while (!topStart && ++cIndex < contourCount);
4251 if (!topStart) {
4252 return NULL;
4253 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004254 while (++cIndex < contourCount) {
4255 contour = contourList[cIndex];
4256 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004257 continue;
4258 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004259 SkScalar testY = SK_ScalarMax;
4260 Segment* test = contour->topSegment(testY);
4261 if (!test || bestY <= testY) {
4262 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004263 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004264 topStart = test;
4265 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004266 }
4267 return topStart;
4268}
4269
caryclark@google.com24bec792012-08-20 12:43:57 +00004270static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4271 int contourCount = contourList.count();
4272 Segment* result;
4273 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4274 Contour* contour = contourList[cIndex];
4275 result = contour->undoneSegment(start, end);
4276 if (result) {
4277 return result;
4278 }
4279 }
4280 return NULL;
4281}
4282
4283
4284
caryclark@google.come21cb182012-07-23 21:26:31 +00004285static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
4286 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004287 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004288 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004289 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4290 Segment* segment = backPtr.fOther;
4291 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004292 SkTDArray<Angle> angles;
4293 int done = 0;
4294 if (segment->activeAngle(tIndex, done, angles)) {
4295 Angle* last = angles.end() - 1;
4296 tIndex = last->start();
4297 endIndex = last->end();
4298 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004299 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004300 if (done == angles.count()) {
4301 chase.pop(&span);
4302 continue;
4303 }
4304 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004305 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004306#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004307 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004308#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004309 if (!sortable) {
4310 chase.pop(&span);
4311 continue;
4312 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004313 // find first angle, initialize winding to computed fWindSum
4314 int firstIndex = -1;
4315 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004316 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004317 do {
4318 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004319 segment = angle->segment();
4320 winding = segment->windSum(angle);
4321 } while (winding == SK_MinS32);
4322 int spanWinding = segment->spanSign(angle->start(), angle->end());
4323 #if DEBUG_WINDING
4324 SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
4325 __FUNCTION__, winding, spanWinding, contourWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00004326 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004327 // turn swinding into contourWinding
4328 if (spanWinding * winding < 0) {
4329 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004330 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004331 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004332 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004333 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004334 // we care about first sign and whether wind sum indicates this
4335 // edge is inside or outside. Maybe need to pass span winding
4336 // or first winding or something into this function?
4337 // advance to first undone angle, then return it and winding
4338 // (to set whether edges are active or not)
4339 int nextIndex = firstIndex + 1;
4340 int angleCount = sorted.count();
4341 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004342 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00004343 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00004344 do {
4345 SkASSERT(nextIndex != firstIndex);
4346 if (nextIndex == angleCount) {
4347 nextIndex = 0;
4348 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004349 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00004350 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004351 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00004352 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004353 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00004354 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
4355 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004356 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004357 tIndex = angle->start();
4358 endIndex = angle->end();
4359 int lesser = SkMin32(tIndex, endIndex);
4360 const Span& nextSpan = segment->span(lesser);
4361 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004362#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00004363 // FIXME: this be wrong. assign startWinding if edge is in
4364 // same direction. If the direction is opposite, winding to
4365 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00004366 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004367 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004368 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004369 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004370#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004371 break;
4372 }
4373 } while (++nextIndex != lastIndex);
4374 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004375 }
4376 return NULL;
4377}
4378
caryclark@google.com027de222012-07-12 12:52:50 +00004379#if DEBUG_ACTIVE_SPANS
4380static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004381 int index;
4382 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00004383 contourList[index]->debugShowActiveSpans();
4384 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004385 for (index = 0; index < contourList.count(); ++ index) {
4386 contourList[index]->validateActiveSpans();
4387 }
caryclark@google.com027de222012-07-12 12:52:50 +00004388}
4389#endif
4390
caryclark@google.com27c449a2012-07-27 18:26:38 +00004391static bool windingIsActive(int winding, int spanWinding) {
4392 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
4393 && (!winding || !spanWinding || winding == -spanWinding);
4394}
4395
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004396// Each segment may have an inside or an outside. Segments contained within
4397// winding may have insides on either side, and form a contour that should be
4398// ignored. Segments that are coincident with opposing direction segments may
4399// have outsides on either side, and should also disappear.
4400// 'Normal' segments will have one inside and one outside. Subsequent connections
4401// when winding should follow the intersection direction. If more than one edge
4402// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004403 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00004404 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004405// returns true if all edges were processed
4406static bool bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004407 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004408 bool unsortable = false;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004409 do {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004410 Segment* topStart = findTopContour(contourList);
caryclark@google.com15fa1382012-05-07 20:49:36 +00004411 if (!topStart) {
4412 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00004413 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004414 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00004415 // follow edges to intersection by changing the index by direction.
4416 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004417 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004418 int contourWinding;
4419 if (firstContour) {
4420 contourWinding = 0;
4421 firstContour = false;
4422 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004423 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004424 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00004425 if (sumWinding == SK_MinS32) {
4426 sumWinding = current->computeSum(index, endIndex);
4427 }
4428 if (sumWinding == SK_MinS32) {
4429 contourWinding = innerContourCheck(contourList, current,
4430 index, endIndex);
4431 } else {
4432 contourWinding = sumWinding;
4433 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004434 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
4435 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00004436 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004437 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004438#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004439 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004440 sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00004441 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004442#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004443 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004444#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004445 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004446 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
4447#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004448 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004449 SkPoint lastPt;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004450 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004451 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004452 // FIXME: needs work. While it works in limited situations, it does
4453 // not always compute winding correctly. Active should be removed and instead
4454 // the initial winding should be correctly passed in so that if the
4455 // inner contour is wound the same way, it never finds an accumulated
4456 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00004457 // other than zero when resolving sorted angles.
caryclark@google.com27c449a2012-07-27 18:26:38 +00004458 bool active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004459 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004460 do {
caryclark@google.com0e08a192012-07-13 21:07:52 +00004461 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00004462 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
4463 __FUNCTION__, active ? "true" : "false",
4464 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004465 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004466 const SkPoint* firstPt = NULL;
4467 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004468 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004469 int nextStart = index;
4470 int nextEnd = endIndex;
4471 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004472 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004473 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00004474 if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
4475 lastPt = current->addCurveTo(index, endIndex, simple, true);
4476 SkASSERT(*firstPt == lastPt);
4477 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004478 break;
4479 }
4480 if (!firstPt) {
4481 firstPt = &current->addMoveTo(index, simple, active);
4482 }
4483 lastPt = current->addCurveTo(index, endIndex, simple, active);
4484 current = next;
4485 index = nextStart;
4486 endIndex = nextEnd;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004487 } while (*firstPt != lastPt && (active || !current->done()));
4488 if (firstPt && active) {
4489 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004490 SkDebugf("path.close();\n");
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004491 #endif
4492 simple.close();
4493 }
caryclark@google.come21cb182012-07-23 21:26:31 +00004494 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004495 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00004496 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004497 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004498 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00004499 break;
4500 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004501 int lesser = SkMin32(index, endIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004502 spanWinding = current->spanSign(index, endIndex);
4503 winding = current->windSum(lesser);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004504 bool inner = useInnerWinding(winding - spanWinding, winding);
4505 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004506 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
caryclark@google.com59823f72012-08-09 18:17:47 +00004507 " inner=%d result=%d\n",
caryclark@google.com2ddff932012-08-07 21:25:27 +00004508 __FUNCTION__, current->debugID(), current->t(lesser),
4509 spanWinding, winding, SkSign32(index - endIndex),
4510 useInnerWinding(winding - spanWinding, winding),
caryclark@google.com2ddff932012-08-07 21:25:27 +00004511 inner ? winding - spanWinding : winding);
4512 #endif
4513 if (inner) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004514 winding -= spanWinding;
4515 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004516 active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004517 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004518 } while (true);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004519 return !unsortable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004520}
4521
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004522// returns true if all edges were processed
4523static bool bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00004524 Segment* current;
4525 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004526 bool unsortable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00004527 while ((current = findUndone(contourList, start, end))) {
4528 const SkPoint* firstPt = NULL;
4529 SkPoint lastPt;
4530 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004531 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004532 int nextStart = start;
4533 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004534 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00004535 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00004536 if (firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
4537 lastPt = current->addCurveTo(start, end, simple, true);
4538 SkASSERT(*firstPt == lastPt);
4539 }
caryclark@google.com24bec792012-08-20 12:43:57 +00004540 break;
4541 }
4542 if (!firstPt) {
4543 firstPt = &current->addMoveTo(start, simple, true);
4544 }
4545 lastPt = current->addCurveTo(start, end, simple, true);
4546 current = next;
4547 start = nextStart;
4548 end = nextEnd;
4549 } while (*firstPt != lastPt);
4550 if (firstPt) {
4551 #if DEBUG_PATH_CONSTRUCTION
4552 SkDebugf("%s close\n", __FUNCTION__);
4553 #endif
4554 simple.close();
4555 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004556 #if DEBUG_ACTIVE_SPANS
4557 debugShowActiveSpans(contourList);
4558 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00004559 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004560 return !unsortable;
caryclark@google.com24bec792012-08-20 12:43:57 +00004561}
4562
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004563static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
4564 int contourCount = contourList.count();
4565 for (int cTest = 0; cTest < contourCount; ++cTest) {
4566 Contour* contour = contourList[cTest];
4567 contour->fixOtherTIndex();
4568 }
4569}
4570
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004571static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004572 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004573 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004574 if (count == 0) {
4575 return;
4576 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004577 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004578 *list.append() = &contours[index];
4579 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004580 QSort<Contour>(list.begin(), list.end() - 1);
4581}
4582
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004583static void assemble(SkPath& simple) {
4584 // TODO: find the non-closed paths and connect them together
4585 SkASSERT(0);
4586}
4587
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004588void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004589 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004590 simple.reset();
4591 simple.setFillType(SkPath::kEvenOdd_FillType);
4592
4593 // turn path into list of segments
4594 SkTArray<Contour> contours;
4595 // FIXME: add self-intersecting cubics' T values to segment
4596 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004597 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004598 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004599 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004600 Contour** currentPtr = contourList.begin();
4601 if (!currentPtr) {
4602 return;
4603 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004604 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004605 // find all intersections between segments
4606 do {
4607 Contour** nextPtr = currentPtr;
4608 Contour* current = *currentPtr++;
4609 Contour* next;
4610 do {
4611 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004612 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004613 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004614 // eat through coincident edges
caryclark@google.com235f56a2012-09-14 14:19:30 +00004615 coincidenceCheck(contourList);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004616 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004617 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004618 if (builder.xorMask() == kWinding_Mask
4619 ? !bridgeWinding(contourList, simple)
4620 : !bridgeXor(contourList, simple))
4621 { // if some edges could not be resolved, assemble remaining fragments
4622 assemble(simple);
caryclark@google.com24bec792012-08-20 12:43:57 +00004623 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004624}
4625