blob: 3f4adf5490b8a252c229246fe034921eed1300f7 [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
caryclark@google.com47580692012-07-23 12:14:49 +000020// An Edge is a Segment generated from a Span
caryclark@google.com15fa1382012-05-07 20:49:36 +000021
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022// FIXME: remove once debugging is complete
caryclark@google.com47580692012-07-23 12:14:49 +000023#ifdef SK_DEBUG
24int gDebugMaxWindSum = SK_MaxS32;
25int gDebugMaxWindValue = SK_MaxS32;
26#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +000027
caryclark@google.comf839c032012-10-26 21:03:50 +000028#define PIN_ADD_T 0
caryclark@google.com0b7da432012-10-31 19:00:20 +000029#define TRY_ROTATE 1
caryclark@google.coma461ff02012-10-11 12:54:23 +000030
caryclark@google.com47580692012-07-23 12:14:49 +000031#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.com6ec15262012-11-16 20:16:50 +000032#define FORCE_RELEASE 0 // set force release to 1 for multiple thread -- no debugging
caryclark@google.comfa0588f2012-04-26 21:01:06 +000033
caryclark@google.com31143cf2012-11-09 22:14:19 +000034#if FORCE_RELEASE || defined SK_RELEASE
caryclark@google.com47580692012-07-23 12:14:49 +000035
36const bool gRunTestsInOneThread = false;
37
38#define DEBUG_ACTIVE_SPANS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000039#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000040#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000041#define DEBUG_ANGLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000042#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000043#define DEBUG_CROSS 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000044#define DEBUG_MARK_DONE 0
caryclark@google.comf839c032012-10-26 21:03:50 +000045#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com47580692012-07-23 12:14:49 +000046#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000047#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000048#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000049
50#else
51
caryclark@google.com47580692012-07-23 12:14:49 +000052const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000053
caryclark@google.comc91dfe42012-10-16 12:06:27 +000054#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000055#define DEBUG_ADD_INTERSECTING_TS 1
56#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000057#define DEBUG_ANGLE 1
58#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000059#define DEBUG_CROSS 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000060#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000061#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000062#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000063#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000064#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000065
66#endif
67
caryclark@google.com6aea33f2012-10-09 14:11:58 +000068#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000069
caryclark@google.comfa0588f2012-04-26 21:01:06 +000070#if DEBUG_DUMP
71static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000072// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000073static int gContourID;
74static int gSegmentID;
75#endif
76
caryclark@google.com8dcf1142012-07-02 20:27:02 +000077#ifndef DEBUG_TEST
78#define DEBUG_TEST 0
79#endif
80
caryclark@google.com32546db2012-08-31 20:55:07 +000081#define MAKE_CONST_LINE(line, pts) \
82 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
83#define MAKE_CONST_QUAD(quad, pts) \
84 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
85 {pts[2].fX, pts[2].fY}}
86#define MAKE_CONST_CUBIC(cubic, pts) \
87 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
88 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
89
caryclark@google.comfa0588f2012-04-26 21:01:06 +000090static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
91 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000092 MAKE_CONST_LINE(aLine, a);
93 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +000094 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
95}
96
97static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
98 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000099 MAKE_CONST_QUAD(aQuad, a);
100 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000101 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000102}
103
caryclark@google.com32546db2012-08-31 20:55:07 +0000104static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000105 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000106 MAKE_CONST_CUBIC(aCubic, a);
107 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000108 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
109}
110
111static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
112 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000113 MAKE_CONST_QUAD(aQuad, a);
114 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000115#define TRY_QUARTIC_SOLUTION 1
116#if TRY_QUARTIC_SOLUTION
117 intersect2(aQuad, bQuad, intersections);
118#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000119 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000120#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000121 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000122}
123
124static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
125 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000126 MAKE_CONST_CUBIC(aCubic, a);
127 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000128 intersect(aCubic, bCubic, intersections);
129 return intersections.fUsed;
130}
131
132static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
133 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000134 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000135 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
136}
137
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000138static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
139 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000140 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000141 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
142}
143
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000144static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
145 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000146 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000147 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
148}
149
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000150static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
151 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000152 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000153 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
154}
155
156static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
157 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000158 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000159 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
160}
161
162static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
163 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000164 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000165 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000166}
167
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000168static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
169 SkScalar , SkScalar , bool , Intersections& ) = {
170 NULL,
171 VLineIntersect,
172 VQuadIntersect,
173 VCubicIntersect
174};
175
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000176static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000177 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000178 double x, y;
179 xy_at_t(line, t, x, y);
180 out->fX = SkDoubleToScalar(x);
181 out->fY = SkDoubleToScalar(y);
182}
183
184static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000185 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000186 double x, y;
187 xy_at_t(quad, t, x, y);
188 out->fX = SkDoubleToScalar(x);
189 out->fY = SkDoubleToScalar(y);
190}
191
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000192static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
193 MAKE_CONST_QUAD(quad, a);
194 xy_at_t(quad, t, out->x, out->y);
195}
196
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000197static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000198 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000199 double x, y;
200 xy_at_t(cubic, t, x, y);
201 out->fX = SkDoubleToScalar(x);
202 out->fY = SkDoubleToScalar(y);
203}
204
205static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
206 NULL,
207 LineXYAtT,
208 QuadXYAtT,
209 CubicXYAtT
210};
211
212static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000213 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000214 double x;
215 xy_at_t(aLine, t, x, *(double*) 0);
216 return SkDoubleToScalar(x);
217}
218
219static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000220 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000221 double x;
222 xy_at_t(quad, t, x, *(double*) 0);
223 return SkDoubleToScalar(x);
224}
225
226static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000227 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000228 double x;
229 xy_at_t(cubic, t, x, *(double*) 0);
230 return SkDoubleToScalar(x);
231}
232
233static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
234 NULL,
235 LineXAtT,
236 QuadXAtT,
237 CubicXAtT
238};
239
240static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000241 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000242 double y;
243 xy_at_t(aLine, t, *(double*) 0, y);
244 return SkDoubleToScalar(y);
245}
246
247static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000248 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000249 double y;
250 xy_at_t(quad, t, *(double*) 0, y);
251 return SkDoubleToScalar(y);
252}
253
254static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000255 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000256 double y;
257 xy_at_t(cubic, t, *(double*) 0, y);
258 return SkDoubleToScalar(y);
259}
260
261static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
262 NULL,
263 LineYAtT,
264 QuadYAtT,
265 CubicYAtT
266};
267
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000268static SkScalar LineDXAtT(const SkPoint a[2], double ) {
269 return a[1].fX - a[0].fX;
270}
271
272static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000273 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000274 double x;
275 dxdy_at_t(quad, t, x, *(double*) 0);
276 return SkDoubleToScalar(x);
277}
278
279static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000280 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000281 double x;
282 dxdy_at_t(cubic, t, x, *(double*) 0);
283 return SkDoubleToScalar(x);
284}
285
286static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
287 NULL,
288 LineDXAtT,
289 QuadDXAtT,
290 CubicDXAtT
291};
292
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000293static void LineSubDivide(const SkPoint a[2], double startT, double endT,
294 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000295 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000296 _Line dst;
297 sub_divide(aLine, startT, endT, dst);
298 sub[0].fX = SkDoubleToScalar(dst[0].x);
299 sub[0].fY = SkDoubleToScalar(dst[0].y);
300 sub[1].fX = SkDoubleToScalar(dst[1].x);
301 sub[1].fY = SkDoubleToScalar(dst[1].y);
302}
303
304static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
305 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000306 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000307 Quadratic dst;
308 sub_divide(aQuad, startT, endT, dst);
309 sub[0].fX = SkDoubleToScalar(dst[0].x);
310 sub[0].fY = SkDoubleToScalar(dst[0].y);
311 sub[1].fX = SkDoubleToScalar(dst[1].x);
312 sub[1].fY = SkDoubleToScalar(dst[1].y);
313 sub[2].fX = SkDoubleToScalar(dst[2].x);
314 sub[2].fY = SkDoubleToScalar(dst[2].y);
315}
316
317static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
318 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000319 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000320 Cubic dst;
321 sub_divide(aCubic, startT, endT, dst);
322 sub[0].fX = SkDoubleToScalar(dst[0].x);
323 sub[0].fY = SkDoubleToScalar(dst[0].y);
324 sub[1].fX = SkDoubleToScalar(dst[1].x);
325 sub[1].fY = SkDoubleToScalar(dst[1].y);
326 sub[2].fX = SkDoubleToScalar(dst[2].x);
327 sub[2].fY = SkDoubleToScalar(dst[2].y);
328 sub[3].fX = SkDoubleToScalar(dst[3].x);
329 sub[3].fY = SkDoubleToScalar(dst[3].y);
330}
331
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000332static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
333 SkPoint []) = {
334 NULL,
335 LineSubDivide,
336 QuadSubDivide,
337 CubicSubDivide
338};
339
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000340static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000341 _Line sub) {
342 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000343 _Line dst;
344 sub_divide(aLine, startT, endT, dst);
345 sub[0] = dst[0];
346 sub[1] = dst[1];
347}
348
349static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000350 Quadratic sub) {
351 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000352 Quadratic dst;
353 sub_divide(aQuad, startT, endT, dst);
354 sub[0] = dst[0];
355 sub[1] = dst[1];
356 sub[2] = dst[2];
357}
358
359static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000360 Cubic sub) {
361 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000362 Cubic dst;
363 sub_divide(aCubic, startT, endT, dst);
364 sub[0] = dst[0];
365 sub[1] = dst[1];
366 sub[2] = dst[2];
367 sub[3] = dst[3];
368}
369
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000370#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000371static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
372 SkRect& bounds) {
373 SkPoint dst[3];
374 QuadSubDivide(a, startT, endT, dst);
375 bounds.fLeft = bounds.fRight = dst[0].fX;
376 bounds.fTop = bounds.fBottom = dst[0].fY;
377 for (int index = 1; index < 3; ++index) {
378 bounds.growToInclude(dst[index].fX, dst[index].fY);
379 }
380}
381
382static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
383 SkRect& bounds) {
384 SkPoint dst[4];
385 CubicSubDivide(a, startT, endT, dst);
386 bounds.fLeft = bounds.fRight = dst[0].fX;
387 bounds.fTop = bounds.fBottom = dst[0].fY;
388 for (int index = 1; index < 4; ++index) {
389 bounds.growToInclude(dst[index].fX, dst[index].fY);
390 }
391}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000392#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000393
caryclark@google.com15fa1382012-05-07 20:49:36 +0000394static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000395 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000396 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000397 Quadratic dst;
398 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000399 if (order == 2) { // quad became line
400 for (int index = 0; index < order; ++index) {
401 SkPoint* pt = reducePts.append();
402 pt->fX = SkDoubleToScalar(dst[index].x);
403 pt->fY = SkDoubleToScalar(dst[index].y);
404 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000405 }
406 return (SkPath::Verb) (order - 1);
407}
408
409static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
410 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000411 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000412 Cubic dst;
413 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000414 if (order == 2 || order == 3) { // cubic became line or quad
415 for (int index = 0; index < order; ++index) {
416 SkPoint* pt = reducePts.append();
417 pt->fX = SkDoubleToScalar(dst[index].x);
418 pt->fY = SkDoubleToScalar(dst[index].y);
419 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000420 }
421 return (SkPath::Verb) (order - 1);
422}
423
caryclark@google.com15fa1382012-05-07 20:49:36 +0000424static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000425 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000426 return isLinear(aQuad, 0, 2);
427}
428
429static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000430 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000431 return isLinear(aCubic, 0, 3);
432}
433
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000434static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000435 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000436 double x[2];
437 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000438 xy_at_t(aLine, endT, x[1], *(double*) 0);
439 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000440}
441
442static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000443 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000444 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000445}
446
447static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000448 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000449 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000450}
451
452static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
453 NULL,
454 LineLeftMost,
455 QuadLeftMost,
456 CubicLeftMost
457};
458
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000459#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000460static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
461 Intersections& intersections) {
462 MAKE_CONST_QUAD(aQuad, a);
463 MAKE_CONST_LINE(bLine, b);
464 return intersectRay(aQuad, bLine, intersections);
465}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000466#endif
467
468static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
469 Intersections& intersections) {
470 MAKE_CONST_QUAD(aQuad, a);
471 return intersectRay(aQuad, bLine, intersections);
472}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000473
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000474class Segment;
475
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000476struct Span {
477 Segment* fOther;
478 mutable SkPoint fPt; // lazily computed as needed
479 double fT;
480 double fOtherT; // value at fOther[fOtherIndex].fT
481 int fOtherIndex; // can't be used during intersection
caryclark@google.com31143cf2012-11-09 22:14:19 +0000482 int fWindSum; // accumulated from contours surrounding this one.
483 int fOppSum; // for binary operators: the opposite winding sum
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000484 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000485 int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000486 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000487 bool fUnsortableStart; // set when start is part of an unsortable pair
488 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.comf839c032012-10-26 21:03:50 +0000489 bool fTiny; // if set, span may still be considered once for edge following
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000490};
491
caryclark@google.com15fa1382012-05-07 20:49:36 +0000492// sorting angles
493// given angles of {dx dy ddx ddy dddx dddy} sort them
494class Angle {
495public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000496 // FIXME: this is bogus for quads and cubics
497 // if the quads and cubics' line from end pt to ctrl pt are coincident,
498 // there's no obvious way to determine the curve ordering from the
499 // derivatives alone. In particular, if one quadratic's coincident tangent
500 // is longer than the other curve, the final control point can place the
501 // longer curve on either side of the shorter one.
502 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
503 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000504
caryclark@google.com32546db2012-08-31 20:55:07 +0000505 /*(
506 for quads and cubics, set up a parameterized line (e.g. LineParameters )
507 for points [0] to [1]. See if point [2] is on that line, or on one side
508 or the other. If it both quads' end points are on the same side, choose
509 the shorter tangent. If the tangents are equal, choose the better second
510 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000511
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000512 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000513 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000514 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000515 double y = dy();
516 double ry = rh.dy();
517 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
518 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000519 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000520 double x = dx();
521 double rx = rh.dx();
522 if (y == 0 && ry == 0 && x * rx < 0) {
523 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000524 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000525 double x_ry = x * ry;
526 double rx_y = rx * y;
527 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000528 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000529 return cmp < 0;
530 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000531 if (approximately_zero(x_ry) && approximately_zero(rx_y)
532 && !approximately_zero_squared(cmp)) {
533 return cmp < 0;
534 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000535 // at this point, the initial tangent line is coincident
caryclark@google.com31143cf2012-11-09 22:14:19 +0000536 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
537 || !approximately_zero(rh.fSide))) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000538 // FIXME: running demo will trigger this assertion
539 // (don't know if commenting out will trigger further assertion or not)
540 // commenting it out allows demo to run in release, though
541 // SkASSERT(fSide != rh.fSide);
542 return fSide < rh.fSide;
543 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000544 // see if either curve can be lengthened and try the tangent compare again
545 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
546 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
547 Angle longer = *this;
548 Angle rhLonger = rh;
549 if (longer.lengthen() | rhLonger.lengthen()) {
550 return longer < rhLonger;
551 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000552 // what if we extend in the other direction?
553 longer = *this;
554 rhLonger = rh;
555 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
556 return longer < rhLonger;
557 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000558 }
caryclark@google.com185c7c42012-10-19 18:26:24 +0000559 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
caryclark@google.com31143cf2012-11-09 22:14:19 +0000560 || (rh.fVerb == SkPath::kLine_Verb
561 && approximately_zero(rx) && approximately_zero(ry))) {
caryclark@google.com185c7c42012-10-19 18:26:24 +0000562 // See general unsortable comment below. This case can happen when
563 // one line has a non-zero change in t but no change in x and y.
564 fUnsortable = true;
565 rh.fUnsortable = true;
566 return this < &rh; // even with no solution, return a stable sort
567 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000568 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
569 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000570 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000571 // end of the shorter tangent to midway between the end points
572 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000573 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
574 double len = fTangent1.normalSquared();
575 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000576 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000577 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000578 int roots, rroots;
579 bool flip = false;
580 do {
581 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
582 double midX = (q[0].x + q[2].x) / 2;
583 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000584 ray[0] = q[1];
585 ray[1].x = midX;
586 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000587 SkASSERT(ray[0] != ray[1]);
588 roots = QuadRayIntersect(fPts, ray, i);
589 rroots = QuadRayIntersect(rh.fPts, ray, ri);
590 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000591 if (roots == 0 || rroots == 0) {
592 // FIXME: we don't have a solution in this case. The interim solution
593 // is to mark the edges as unsortable, exclude them from this and
594 // future computations, and allow the returned path to be fragmented
595 fUnsortable = true;
596 rh.fUnsortable = true;
597 return this < &rh; // even with no solution, return a stable sort
598 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000599 _Point loc;
600 double best = SK_ScalarInfinity;
601 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000602 int index;
603 for (index = 0; index < roots; ++index) {
604 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000605 dx = loc.x - ray[0].x;
606 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000607 dist = dx * dx + dy * dy;
608 if (best > dist) {
609 best = dist;
610 }
611 }
612 for (index = 0; index < rroots; ++index) {
613 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000614 dx = loc.x - ray[0].x;
615 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000616 dist = dx * dx + dy * dy;
617 if (best > dist) {
618 return fSide < 0;
619 }
620 }
621 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000622 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000623
caryclark@google.com47580692012-07-23 12:14:49 +0000624 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000625 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000626 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000627
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000628 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000629 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000630 }
631
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000632 int end() const {
633 return fEnd;
634 }
635
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000636 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000637 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000638 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000639
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000640 bool lengthen() {
641 int newEnd = fEnd;
642 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
643 fEnd = newEnd;
644 setSpans();
645 return true;
646 }
647 return false;
648 }
649
caryclark@google.coma461ff02012-10-11 12:54:23 +0000650 bool reverseLengthen() {
651 if (fReversed) {
652 return false;
653 }
654 int newEnd = fStart;
655 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
656 fEnd = newEnd;
657 fReversed = true;
658 setSpans();
659 return true;
660 }
661 return false;
662 }
663
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000664 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000665 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000666 fSegment = segment;
667 fStart = start;
668 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000669 fPts = orig;
670 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000671 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000672 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000673 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000674 setSpans();
675 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000676
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000677 void setSpans() {
678 double startT = (*fSpans)[fStart].fT;
679 double endT = (*fSpans)[fEnd].fT;
680 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000681 case SkPath::kLine_Verb:
682 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000683 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000684 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
685 fTangent1.lineEndPoints(l);
caryclark@google.comf839c032012-10-26 21:03:50 +0000686 fUnsortable = dx() == 0 && dy() == 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000687 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000688 break;
689 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000690 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000691 fTangent1.quadEndPoints(fQ, 0, 1);
692 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000693 break;
694 case SkPath::kCubic_Verb:
695 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000696 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000697 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000698 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000699 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000700 default:
701 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000702 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000703 if (fUnsortable) {
704 return;
705 }
706 SkASSERT(fStart != fEnd);
707 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
708 for (int index = fStart; index != fEnd; index += step) {
709 if ((*fSpans)[index].fUnsortableStart) {
710 fUnsortable = true;
711 return;
712 }
713 if (index != fStart && (*fSpans)[index].fUnsortableEnd) {
714 fUnsortable = true;
715 return;
716 }
717 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000718 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000719
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000720 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000721 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000722 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000723
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000724 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000725 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000726 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000727
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000728 const SkTDArray<Span>* spans() const {
729 return fSpans;
730 }
731
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000732 int start() const {
733 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000734 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000735
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000736 bool unsortable() const {
737 return fUnsortable;
738 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000739
caryclark@google.comc899ad92012-08-23 15:24:42 +0000740#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000741 const SkPoint* pts() const {
742 return fPts;
743 }
744
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000745 SkPath::Verb verb() const {
746 return fVerb;
747 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000748
caryclark@google.comc899ad92012-08-23 15:24:42 +0000749 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000750 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000751 }
752#endif
753
caryclark@google.com15fa1382012-05-07 20:49:36 +0000754private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000755 const SkPoint* fPts;
756 Quadratic fQ;
757 SkPath::Verb fVerb;
758 double fSide;
759 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000760 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000761 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000762 int fStart;
763 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000764 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000765 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000766};
767
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000768// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000769struct Bounds : public SkRect {
770 static bool Intersects(const Bounds& a, const Bounds& b) {
771 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
772 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
773 }
774
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000775 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
776 if (left < fLeft) {
777 fLeft = left;
778 }
779 if (top < fTop) {
780 fTop = top;
781 }
782 if (right > fRight) {
783 fRight = right;
784 }
785 if (bottom > fBottom) {
786 fBottom = bottom;
787 }
788 }
789
790 void add(const Bounds& toAdd) {
791 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
792 }
793
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000794 bool isEmpty() {
795 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000796 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000797 || isnan(fLeft) || isnan(fRight)
798 || isnan(fTop) || isnan(fBottom);
799 }
800
801 void setCubicBounds(const SkPoint a[4]) {
802 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000803 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000804 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000805 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
806 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000807 }
808
809 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000810 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000811 _Rect dRect;
812 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000813 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
814 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000815 }
816};
817
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000818// OPTIMIZATION: does the following also work, and is it any faster?
819// return outerWinding * innerWinding > 0
820// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
caryclark@google.com2ddff932012-08-07 21:25:27 +0000821static bool useInnerWinding(int outerWinding, int innerWinding) {
822 SkASSERT(outerWinding != innerWinding);
823 int absOut = abs(outerWinding);
824 int absIn = abs(innerWinding);
825 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
826 if (outerWinding * innerWinding < 0) {
827#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000828 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000829 outerWinding, innerWinding, result ? "true" : "false");
830#endif
831 }
832 return result;
833}
834
caryclark@google.com31143cf2012-11-09 22:14:19 +0000835static const bool gOpLookup[][2][2] = {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000836 // ==0 !=0
837 // b a b a
838 {{true , false}, {false, true }}, // a - b
839 {{false, false}, {true , true }}, // a & b
840 {{true , true }, {false, false}}, // a | b
841 {{true , true }, {true , true }}, // a ^ b
842};
843
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000844static bool activeOp(bool angleIsOp, int otherNonZero, int otherCoin, ShapeOp op) {
845 if (otherNonZero != otherCoin) {
846 return op == kIntersect_Op || op == kUnion_Op;
847 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000848 return gOpLookup[op][otherNonZero][angleIsOp];
caryclark@google.com235f56a2012-09-14 14:19:30 +0000849}
850
caryclark@google.comf839c032012-10-26 21:03:50 +0000851// wrap path to keep track of whether the contour is initialized and non-empty
852class PathWrapper {
853public:
854 PathWrapper(SkPath& path)
855 : fPathPtr(&path)
856 {
857 init();
858 }
859
860 void close() {
861 if (!fHasMove) {
862 return;
863 }
864 bool callClose = isClosed();
865 lineTo();
866 if (fEmpty) {
867 return;
868 }
869 if (callClose) {
870 #if DEBUG_PATH_CONSTRUCTION
871 SkDebugf("path.close();\n");
872 #endif
873 fPathPtr->close();
874 }
875 init();
876 }
877
878 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
879 lineTo();
880 moveTo();
881#if DEBUG_PATH_CONSTRUCTION
882 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
883 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
884#endif
885 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
886 fDefer[0] = fDefer[1] = pt3;
887 fEmpty = false;
888 }
889
890 void deferredLine(const SkPoint& pt) {
891 if (pt == fDefer[1]) {
892 return;
893 }
894 if (changedSlopes(pt)) {
895 lineTo();
896 fDefer[0] = fDefer[1];
897 }
898 fDefer[1] = pt;
899 }
900
901 void deferredMove(const SkPoint& pt) {
902 fMoved = true;
903 fHasMove = true;
904 fEmpty = true;
905 fDefer[0] = fDefer[1] = pt;
906 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000907
caryclark@google.comf839c032012-10-26 21:03:50 +0000908 void deferredMoveLine(const SkPoint& pt) {
909 if (!fHasMove) {
910 deferredMove(pt);
911 }
912 deferredLine(pt);
913 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000914
caryclark@google.comf839c032012-10-26 21:03:50 +0000915 bool hasMove() const {
916 return fHasMove;
917 }
918
919 void init() {
920 fEmpty = true;
921 fHasMove = false;
922 fMoved = false;
923 }
924
925 bool isClosed() const {
926 return !fEmpty && fFirstPt == fDefer[1];
927 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000928
caryclark@google.comf839c032012-10-26 21:03:50 +0000929 void lineTo() {
930 if (fDefer[0] == fDefer[1]) {
931 return;
932 }
933 moveTo();
934 fEmpty = false;
935#if DEBUG_PATH_CONSTRUCTION
936 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
937#endif
938 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
939 fDefer[0] = fDefer[1];
940 }
941
942 const SkPath* nativePath() const {
943 return fPathPtr;
944 }
945
946 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
947 lineTo();
948 moveTo();
949#if DEBUG_PATH_CONSTRUCTION
950 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
951 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
952#endif
953 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
954 fDefer[0] = fDefer[1] = pt2;
955 fEmpty = false;
956 }
957
958protected:
959 bool changedSlopes(const SkPoint& pt) const {
960 if (fDefer[0] == fDefer[1]) {
961 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000962 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000963 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
964 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
965 SkScalar lineDx = pt.fX - fDefer[1].fX;
966 SkScalar lineDy = pt.fY - fDefer[1].fY;
967 return deferDx * lineDy != deferDy * lineDx;
968 }
969
970 void moveTo() {
971 if (!fMoved) {
972 return;
973 }
974 fFirstPt = fDefer[0];
975#if DEBUG_PATH_CONSTRUCTION
976 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
977#endif
978 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
979 fMoved = false;
980 }
981
982private:
983 SkPath* fPathPtr;
984 SkPoint fDefer[2];
985 SkPoint fFirstPt;
986 bool fEmpty;
987 bool fHasMove;
988 bool fMoved;
989};
990
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000991class Segment {
992public:
993 Segment() {
994#if DEBUG_DUMP
995 fID = ++gSegmentID;
996#endif
997 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +0000998
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000999 bool operator<(const Segment& rh) const {
1000 return fBounds.fTop < rh.fBounds.fTop;
1001 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001002
caryclark@google.com9764cc62012-07-12 19:29:45 +00001003 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
1004 if (activeAngleInner(index, done, angles)) {
1005 return true;
1006 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001007 double referenceT = fTs[index].fT;
1008 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001009 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001010 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001011 return true;
1012 }
1013 }
1014 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001015 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001016 return true;
1017 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001018 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001019 return false;
1020 }
1021
caryclark@google.com9764cc62012-07-12 19:29:45 +00001022 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001023 Span* span = &fTs[index];
1024 Segment* other = span->fOther;
1025 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001026 return other->activeAngleInner(oIndex, done, angles);
1027 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001028
caryclark@google.com9764cc62012-07-12 19:29:45 +00001029 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001030 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001031 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001032 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001033 if (upSpan.fWindValue) {
1034 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001035 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001036 done++;
1037 } else if (upSpan.fWindSum != SK_MinS32) {
1038 return true;
1039 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001040 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001041 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001042 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001043 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001044 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001045 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001046 if (downSpan.fWindValue) {
1047 addAngle(angles, index, prev);
1048 if (downSpan.fDone) {
1049 done++;
1050 } else if (downSpan.fWindSum != SK_MinS32) {
1051 return true;
1052 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001053 }
1054 }
1055 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001056 }
1057
caryclark@google.comf839c032012-10-26 21:03:50 +00001058 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001059 SkASSERT(!done());
1060 int count = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001061 result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001062 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001063 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001064 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001065 const Span& span = fTs[index];
1066 if (span.fUnsortableStart | lastUnsortable) {
1067 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001068 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001069 if (!span.fDone | !lastDone) {
1070 const SkPoint& xy = xyAtT(index);
1071 if (result.fY < xy.fY) {
1072 goto next;
1073 }
1074 if (result.fY == xy.fY && result.fX < xy.fX) {
1075 goto next;
1076 }
1077 result = xy;
1078 }
1079 next:
1080 lastDone = span.fDone;
1081 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001082 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001083 SkASSERT(result.fY < SK_ScalarMax);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001084 }
1085
1086 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001087 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001088 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001089#if DEBUG_ANGLE
caryclark@google.com31143cf2012-11-09 22:14:19 +00001090 if (angles.count() > 1 && !fTs[start].fTiny) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001091 SkPoint angle0Pt, newPt;
1092 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1093 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1094 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1095 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1096 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1097 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001098#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001099 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001100 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001101
caryclark@google.com2ddff932012-08-07 21:25:27 +00001102 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001103 double oEnd) {
1104 int tIndex = -1;
1105 int tCount = fTs.count();
1106 int oIndex = -1;
1107 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001108 do {
1109 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001110 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001111 int tIndexStart = tIndex;
1112 do {
1113 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001114 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001115 int oIndexStart = oIndex;
1116 double nextT;
1117 do {
1118 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001119 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001120 double oNextT;
1121 do {
1122 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001123 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001124 // at this point, spans before and after are at:
1125 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1126 // if tIndexStart == 0, no prior span
1127 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001128
caryclark@google.comcc905052012-07-25 20:59:42 +00001129 // advance the span with zero winding
1130 // if the following span exists (not past the end, non-zero winding)
1131 // connect the two edges
1132 if (!fTs[tIndexStart].fWindValue) {
1133 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1134 #if DEBUG_CONCIDENT
1135 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1136 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001137 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1138 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001139 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001140 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001141 }
1142 if (nextT < 1 && fTs[tIndex].fWindValue) {
1143 #if DEBUG_CONCIDENT
1144 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1145 __FUNCTION__, fID, other.fID, tIndex,
1146 fTs[tIndex].fT, xyAtT(tIndex).fX,
1147 xyAtT(tIndex).fY);
1148 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001149 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001150 }
1151 } else {
1152 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1153 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1154 #if DEBUG_CONCIDENT
1155 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1156 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001157 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1158 other.xyAtT(oIndexStart).fY);
1159 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001160 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001161 }
1162 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1163 #if DEBUG_CONCIDENT
1164 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1165 __FUNCTION__, fID, other.fID, oIndex,
1166 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1167 other.xyAtT(oIndex).fY);
1168 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1169 #endif
1170 }
1171 }
1172 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001173
caryclark@google.comcc905052012-07-25 20:59:42 +00001174 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1175 double oEnd) {
1176 // walk this to outsideTs[0]
1177 // walk other to outsideTs[1]
1178 // if either is > 0, add a pointer to the other, copying adjacent winding
1179 int tIndex = -1;
1180 int oIndex = -1;
1181 double tStart = outsideTs[0];
1182 double oStart = outsideTs[1];
1183 do {
1184 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001185 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001186 do {
1187 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001188 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001189 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001190 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001191 }
1192 tStart = fTs[tIndex].fT;
1193 oStart = other.fTs[oIndex].fT;
1194 do {
1195 double nextT;
1196 do {
1197 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001198 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001199 tStart = nextT;
1200 do {
1201 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001202 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001203 oStart = nextT;
1204 if (tStart == 1 && oStart == 1) {
1205 break;
1206 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001207 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001208 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001209 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001210
caryclark@google.com235f56a2012-09-14 14:19:30 +00001211 void addCubic(const SkPoint pts[4], bool operand) {
1212 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001213 fBounds.setCubicBounds(pts);
1214 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001215
caryclark@google.comf839c032012-10-26 21:03:50 +00001216 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001217 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001218 const SkPoint* ePtr;
1219 int lastT = fTs.count() - 1;
1220 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1221 ePtr = fPts;
1222 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001223 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001224 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1225 ePtr = edge;
1226 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001227 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001228 bool reverse = ePtr == fPts && start != 0;
1229 if (reverse) {
1230 path.deferredMoveLine(ePtr[fVerb]);
1231 switch (fVerb) {
1232 case SkPath::kLine_Verb:
1233 path.deferredLine(ePtr[0]);
1234 break;
1235 case SkPath::kQuad_Verb:
1236 path.quadTo(ePtr[1], ePtr[0]);
1237 break;
1238 case SkPath::kCubic_Verb:
1239 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1240 break;
1241 default:
1242 SkASSERT(0);
1243 }
1244 // return ePtr[0];
1245 } else {
1246 path.deferredMoveLine(ePtr[0]);
1247 switch (fVerb) {
1248 case SkPath::kLine_Verb:
1249 path.deferredLine(ePtr[1]);
1250 break;
1251 case SkPath::kQuad_Verb:
1252 path.quadTo(ePtr[1], ePtr[2]);
1253 break;
1254 case SkPath::kCubic_Verb:
1255 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1256 break;
1257 default:
1258 SkASSERT(0);
1259 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001260 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001261 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001262 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001263 }
1264
caryclark@google.com235f56a2012-09-14 14:19:30 +00001265 void addLine(const SkPoint pts[2], bool operand) {
1266 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001267 fBounds.set(pts, 2);
1268 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001269
caryclark@google.comf839c032012-10-26 21:03:50 +00001270#if 0
1271 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001272 const SkPoint& pt = xyAtT(tIndex);
1273 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001274 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001275 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001276 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001277 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001278#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001279
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001280 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001281 void addOtherT(int index, double otherT, int otherIndex) {
1282 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001283 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001284 if (precisely_less_than_zero(otherT)) {
1285 otherT = 0;
1286 } else if (precisely_greater_than_one(otherT)) {
1287 otherT = 1;
1288 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001289 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001290 span.fOtherT = otherT;
1291 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001292 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001293
caryclark@google.com235f56a2012-09-14 14:19:30 +00001294 void addQuad(const SkPoint pts[3], bool operand) {
1295 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001296 fBounds.setQuadBounds(pts);
1297 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001298
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001299 // Defer all coincident edge processing until
1300 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001301
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001302// no need to be tricky; insert in normal T order
1303// resolve overlapping ts when considering coincidence later
1304
1305 // add non-coincident intersection. Resulting edges are sorted in T.
1306 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001307 // FIXME: in the pathological case where there is a ton of intercepts,
1308 // binary search?
1309 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001310 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001311 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001312 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001313 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001314 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001315 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001316 newT = 1;
1317 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001318 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001319 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001320 // OPTIMIZATION: if there are three or more identical Ts, then
1321 // the fourth and following could be further insertion-sorted so
1322 // that all the edges are clockwise or counterclockwise.
1323 // This could later limit segment tests to the two adjacent
1324 // neighbors, although it doesn't help with determining which
1325 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001326 if (newT < fTs[index].fT) {
1327 insertedAt = index;
1328 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001329 }
1330 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001331 Span* span;
1332 if (insertedAt >= 0) {
1333 span = fTs.insert(insertedAt);
1334 } else {
1335 insertedAt = tCount;
1336 span = fTs.append();
1337 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001338 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001339 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001340 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001341 span->fWindSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001342 span->fOppSum = SK_MinS32;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001343 span->fWindValue = 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001344 span->fOppValue = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001345 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001346 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001347 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001348 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001349 span->fUnsortableStart = false;
1350 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001351 if (span - fTs.begin() > 0 && !span[-1].fDone
1352 && !precisely_negative(newT - span[-1].fT)
1353 // && approximately_negative(newT - span[-1].fT)
1354 && xyAtT(&span[-1]) == xyAtT(span)) {
1355 span[-1].fTiny = true;
1356 span[-1].fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001357 if (approximately_negative(newT - span[-1].fT)) {
1358 if (approximately_greater_than_one(newT)) {
1359 span[-1].fUnsortableStart = true;
1360 span[-2].fUnsortableEnd = true;
1361 }
1362 if (approximately_less_than_zero(span[-1].fT)) {
1363 span->fUnsortableStart = true;
1364 span[-1].fUnsortableEnd = true;
1365 }
1366 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001367 ++fDoneSpans;
1368 }
1369 if (fTs.end() - span > 1 && !span->fDone
1370 && !precisely_negative(span[1].fT - newT)
1371 // && approximately_negative(span[1].fT - newT)
1372 && xyAtT(&span[1]) == xyAtT(span)) {
1373 span->fTiny = true;
1374 span->fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001375 if (approximately_negative(span[1].fT - newT)) {
1376 if (approximately_greater_than_one(span[1].fT)) {
1377 span->fUnsortableStart = true;
1378 span[-1].fUnsortableEnd = true;
1379 }
1380 if (approximately_less_than_zero(newT)) {
1381 span[1].fUnsortableStart = true;
1382 span->fUnsortableEnd = true;
1383 }
1384 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001385 ++fDoneSpans;
1386 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001387 return insertedAt;
1388 }
1389
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001390 // set spans from start to end to decrement by one
1391 // note this walks other backwards
1392 // FIMXE: there's probably an edge case that can be constructed where
1393 // two span in one segment are separated by float epsilon on one span but
1394 // not the other, if one segment is very small. For this
1395 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001396 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001397 // sorted? It feels better in such a case to match the span's other span
1398 // pointer since both coincident segments must contain the same spans.
1399 void addTCancel(double startT, double endT, Segment& other,
1400 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001401 SkASSERT(!approximately_negative(endT - startT));
1402 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001403 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001404 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001405 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001406 ++index;
1407 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001408 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001409 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001410 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001411 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001412 Span* test = &fTs[index];
1413 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001414 SkTDArray<double> outsideTs;
1415 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001416 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001417 bool decrement = test->fWindValue && oTest->fWindValue && !binary;
caryclark@google.comcc905052012-07-25 20:59:42 +00001418 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001419 double testT = test->fT;
1420 double oTestT = oTest->fT;
1421 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001422 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001423 if (decrement) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001424 decrementSpan(span);
caryclark@google.com200c2112012-08-03 15:05:04 +00001425 } else if (track && span->fT < 1 && oTestT < 1) {
1426 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001427 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001428 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001429 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001430 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001431 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1432 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1433 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001434 while (approximately_negative(otherTMatchStart - oSpan->fT)
1435 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001436 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001437 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001438 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001439 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001440 other.decrementSpan(oSpan);
1441 } else if (track && oSpan->fT < 1 && testT < 1) {
1442 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001443 }
1444 if (!oIndex) {
1445 break;
1446 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001447 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001448 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001449 test = span;
1450 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001451 } while (!approximately_negative(endT - test->fT));
1452 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001453 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001454 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001455 double tStart = outsideTs[0];
1456 double oStart = outsideTs[1];
1457 addCancelOutsides(tStart, oStart, other, oEndT);
1458 int count = outsideTs.count();
1459 if (count > 2) {
1460 double tStart = outsideTs[count - 2];
1461 double oStart = outsideTs[count - 1];
1462 addCancelOutsides(tStart, oStart, other, oEndT);
1463 }
caryclark@google.com18063442012-07-25 12:05:18 +00001464 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001465 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001466 double tStart = oOutsideTs[0];
1467 double oStart = oOutsideTs[1];
1468 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001469 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001470 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00001471
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001472 int bumpCoincidentThis(const Span* oTest, const bool transfer, const bool decrementThis,
1473 const bool thisXor, const bool opp, int index, SkTDArray<double>& outsideTs,
1474 SkTDArray<double>& xOutsideTs)
1475 {
1476 Span* const test = &fTs[index];
1477 Span* end = test;
1478 const int startIndex = index;
1479 const double oStartT = oTest->fT;
1480 do {
1481 if (transfer) {
caryclark@google.com6ec15262012-11-16 20:16:50 +00001482 if (opp) {
1483 if (decrementThis) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001484 zeroSpan(end);
1485 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001486 } else {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001487 end->fWindValue += oTest->fOppValue;
caryclark@google.com6ec15262012-11-16 20:16:50 +00001488 end->fOppValue += oTest->fWindValue;
1489 }
1490 } else if (!decrementThis & !thisXor) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001491 #ifdef SK_DEBUG
1492 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
1493 #endif
1494 ++(end->fWindValue);
1495 } else if (decrementSpan(end)) {
1496 TrackOutside(outsideTs, end->fT, oStartT);
1497 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001498 } else if (oTest->fWindValue) {
1499 SkASSERT(decrementThis);
1500 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1501 TrackOutside(xOutsideTs, end->fT, oStartT);
1502 }
1503 }
1504 end = &fTs[++index];
1505 } while (approximately_negative(end->fT - test->fT));
1506 return index;
1507 }
1508
1509 // because of the order in which coincidences are resolved, this and other
1510 // may not have the same intermediate points. Compute the corresponding
1511 // intermediate T values (using this as the master, other as the follower)
1512 // and walk other conditionally -- hoping that it catches up in the end
1513 int bumpCoincidentOther(const Span* test, const bool transfer, const bool decrementThis,
1514 const bool otherXor, const bool opp, const double tRatio, const double oEndT,
1515 int& oIndex, SkTDArray<double>& oOutsideTs)
1516 {
1517 Span* const oTest = &fTs[oIndex];
1518 Span* oEnd = oTest;
1519 const double startT = test->fT;
1520 const int oStartIndex = oIndex;
1521 const double oStartT = oTest->fT;
1522 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
1523 while (!approximately_negative(oEndT - oEnd->fT)
1524 && approximately_negative(oEnd->fT - otherTMatch)) {
1525 if (transfer) {
caryclark@google.com6ec15262012-11-16 20:16:50 +00001526 if (opp) {
1527 if (decrementThis) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001528 oEnd->fWindValue += test->fOppValue;
caryclark@google.com6ec15262012-11-16 20:16:50 +00001529 oEnd->fOppValue += test->fWindValue;
1530 } else {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001531 zeroSpan(oEnd);
1532 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001533 }
1534 } else if (decrementThis & !otherXor & !opp) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001535 #ifdef SK_DEBUG
1536 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1537 #endif
1538 ++(oEnd->fWindValue);
1539 } else if (decrementSpan(oEnd)) {
1540 TrackOutside(oOutsideTs, oEnd->fT, startT);
1541 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001542 } else if (test->fWindValue) {
1543 SkASSERT(decrementThis);
1544 if (oStartIndex > 0 && fTs[oStartIndex - 1].fWindValue) {
1545 SkASSERT(0); // track for later?
1546 }
1547 }
1548 oEnd = &fTs[++oIndex];
1549 }
1550 return oIndex;
1551 }
1552
1553 // FIXME: need to test this case:
1554 // contourA has two segments that are coincident
1555 // contourB has two segments that are coincident in the same place
1556 // each ends up with +2/0 pairs for winding count
1557 // since logic below doesn't transfer count (only increments/decrements) can this be
1558 // resolved to +4/0 ?
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001559
1560 // set spans from start to end to increment the greater by one and decrement
1561 // the lesser
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001562 void addTCoincident(bool thisXor, bool otherXor, double startT, double endT,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001563 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001564 SkASSERT(!approximately_negative(endT - startT));
1565 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001566 bool opp = fOperand ^ other.fOperand;
1567 if (!opp) {
1568 otherXor = thisXor;
1569 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001570 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001571 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001572 ++index;
1573 }
1574 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001575 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001576 ++oIndex;
1577 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001578 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001579 Span* test = &fTs[index];
1580 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001581 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001582 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001583 SkTDArray<double> oOutsideTs;
1584 do {
caryclark@google.com6ec15262012-11-16 20:16:50 +00001585 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001586 bool decrementThis = test->fWindValue < oTest->fWindValue;
1587 if (decrementThis) {
1588 oIndex = other.bumpCoincidentOther(test, transfer, decrementThis, otherXor, opp,
1589 tRatio, oEndT, oIndex, oOutsideTs);
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00001590 index = bumpCoincidentThis(oTest, transfer, decrementThis, thisXor, opp,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001591 index, outsideTs, xOutsideTs);
1592 } else {
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00001593 index = bumpCoincidentThis(oTest, transfer, decrementThis, thisXor, opp,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001594 index, outsideTs, xOutsideTs);
1595 oIndex = other.bumpCoincidentOther(test, transfer, decrementThis, otherXor, opp,
1596 tRatio, oEndT, oIndex, oOutsideTs);
caryclark@google.com59823f72012-08-09 18:17:47 +00001597 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001598 test = &fTs[index];
1599 oTest = &other.fTs[oIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001600 } while (!approximately_negative(endT - test->fT));
1601 SkASSERT(approximately_negative(oTest->fT - oEndT));
1602 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001603 if (!done()) {
1604 if (outsideTs.count()) {
1605 addCoinOutsides(outsideTs, other, oEndT);
1606 }
1607 if (xOutsideTs.count()) {
1608 addCoinOutsides(xOutsideTs, other, oEndT);
1609 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001610 }
1611 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001612 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001613 }
1614 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001615
caryclark@google.comcc905052012-07-25 20:59:42 +00001616 // FIXME: this doesn't prevent the same span from being added twice
1617 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001618 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001619 int tCount = fTs.count();
1620 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1621 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001622 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001623 break;
1624 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001625 if (approximately_negative(span.fT - t) && span.fOther == &other
1626 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001627#if DEBUG_ADD_T_PAIR
1628 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1629 __FUNCTION__, fID, t, other.fID, otherT);
1630#endif
1631 return;
1632 }
1633 }
caryclark@google.com47580692012-07-23 12:14:49 +00001634#if DEBUG_ADD_T_PAIR
1635 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1636 __FUNCTION__, fID, t, other.fID, otherT);
1637#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001638 int insertedAt = addT(t, &other);
1639 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001640 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001641 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001642 matchWindingValue(insertedAt, t, borrowWind);
1643 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001644 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001645
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001646 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001647 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001648 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1649 addAngle(angles, end, start);
1650 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001651 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001652 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001653 int tIndex = nextExactSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001654 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001655 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001656 }
1657 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001658
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001659 const Bounds& bounds() const {
1660 return fBounds;
1661 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001662
caryclark@google.com31143cf2012-11-09 22:14:19 +00001663 void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001664 double referenceT = fTs[index].fT;
1665 int lesser = index;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001666 while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand)
1667 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00001668 buildAnglesInner(lesser, angles);
1669 }
1670 do {
1671 buildAnglesInner(index, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001672 } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand)
1673 && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001674 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001675
1676 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1677 Span* span = &fTs[index];
1678 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001679 // if there is only one live crossing, and no coincidence, continue
1680 // in the same direction
1681 // if there is coincidence, the only choice may be to reverse direction
1682 // find edge on either side of intersection
1683 int oIndex = span->fOtherIndex;
1684 // if done == -1, prior span has already been processed
1685 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001686 int next = other->nextExactSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001687 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001688 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001689 next = other->nextExactSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001690 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001691 // add candidate into and away from junction
1692 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001693 }
1694
caryclark@google.com6ec15262012-11-16 20:16:50 +00001695 int computeSum(int startIndex, int endIndex, int* oppoSum) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001696 SkTDArray<Angle> angles;
1697 addTwoAngles(startIndex, endIndex, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001698 buildAngles(endIndex, angles, false);
caryclark@google.comd1688742012-09-18 20:08:37 +00001699 // OPTIMIZATION: check all angles to see if any have computed wind sum
1700 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001701 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001702 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001703#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001704 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001705#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001706 if (!sortable) {
1707 return SK_MinS32;
1708 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001709 int angleCount = angles.count();
1710 const Angle* angle;
1711 const Segment* base;
1712 int winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001713 int oWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001714 int firstIndex = 0;
1715 do {
1716 angle = sorted[firstIndex];
1717 base = angle->segment();
1718 winding = base->windSum(angle);
1719 if (winding != SK_MinS32) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001720 oWinding = base->oppSum(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001721 break;
1722 }
1723 if (++firstIndex == angleCount) {
1724 return SK_MinS32;
1725 }
1726 } while (true);
1727 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001728 int spanWinding = base->spanSign(angle);
1729 bool inner = useInnerWinding(winding + spanWinding, winding);
1730 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001731 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001732 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001733 inner ? winding + spanWinding : winding);
1734 #endif
1735 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001736 winding += spanWinding;
1737 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001738 int oppoWinding = base->oppSign(angle);
1739 if (useInnerWinding(oWinding + oppoWinding, oWinding)) {
1740 oWinding += oppoWinding;
1741 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001742 #if DEBUG_SORT
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001743 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001744 #endif
1745 int nextIndex = firstIndex + 1;
1746 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001747 winding -= base->spanSign(angle);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001748 oWinding -= base->oppSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001749 do {
1750 if (nextIndex == angleCount) {
1751 nextIndex = 0;
1752 }
1753 angle = sorted[nextIndex];
1754 Segment* segment = angle->segment();
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001755 bool opp = base->fOperand ^ segment->fOperand;
1756 int maxWinding, oMaxWinding;
1757 int spanSign = segment->spanSign(angle);
1758 int oppoSign = segment->oppSign(angle);
1759 if (opp) {
1760 oMaxWinding = oWinding;
1761 oWinding -= spanSign;
1762 if (oppoSign) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001763 maxWinding = winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001764 winding -= oppoSign;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001765 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001766 } else {
1767 maxWinding = winding;
1768 winding -= spanSign;
1769 if (oppoSign) {
1770 oMaxWinding = oWinding;
1771 oWinding -= oppoSign;
1772 }
1773 }
1774 if (segment->windSum(angle) == SK_MinS32) {
1775 if (opp) {
1776 if (useInnerWinding(oMaxWinding, oWinding)) {
1777 oMaxWinding = oWinding;
1778 }
1779 if (oppoSign && useInnerWinding(maxWinding, winding)) {
1780 maxWinding = winding;
1781 }
1782 segment->markAndChaseWinding(angle, oMaxWinding, maxWinding);
1783 } else {
1784 if (useInnerWinding(maxWinding, winding)) {
1785 maxWinding = winding;
1786 }
1787 if (oppoSign && useInnerWinding(oMaxWinding, oWinding)) {
1788 oMaxWinding = oWinding;
1789 }
1790 segment->markAndChaseWinding(angle, maxWinding, oMaxWinding);
1791 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001792 }
1793 } while (++nextIndex != lastIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001794 int minIndex = SkMin32(startIndex, endIndex);
1795 if (oppoSum) {
1796 *oppoSum = oppSum(minIndex);
1797 }
1798 return windSum(minIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001799 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001800
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001801 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001802 int bestT = -1;
1803 SkScalar top = bounds().fTop;
1804 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001805 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001806 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001807 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001808 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001809 if (fTs[start].fWindValue == 0) {
1810 continue;
1811 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001812 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001813 double startT = fTs[start].fT;
1814 double endT = fTs[end].fT;
1815 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001816 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001817 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001818 // FIXME: always use original and limit results to T values within
1819 // start t and end t.
1820 // OPTIMIZE: use specialty function that intersects ray with curve,
1821 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001822 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1823 false, intersections);
1824 if (pts == 0) {
1825 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001826 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001827 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1828 // if the intersection is edge on, wait for another one
1829 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001830 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001831 for (int index = 0; index < pts; ++index) {
1832 SkPoint pt;
1833 double foundT = intersections.fT[0][index];
1834 double testT = startT + (endT - startT) * foundT;
1835 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1836 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001837 if (fVerb > SkPath::kLine_Verb
1838 && !approximately_less_than_zero(foundT)
1839 && !approximately_greater_than_one(foundT)) {
1840 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1841 if (approximately_zero(dx)) {
1842 continue;
1843 }
1844 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001845 bestY = pt.fY;
1846 bestT = foundT < 1 ? start : end;
1847 hitT = testT;
1848 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001849 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001850 } while (fTs[end].fT != 1);
1851 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001852 }
caryclark@google.com18063442012-07-25 12:05:18 +00001853
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001854 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1855 // if a segment is connected to this one, consider it crossing
1856 int tIndex;
1857 if (fPts[0].fX == basePt.fX) {
1858 tIndex = 0;
1859 do {
1860 const Span& sSpan = fTs[tIndex];
1861 const Segment* sOther = sSpan.fOther;
1862 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1863 continue;
1864 }
1865 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1866 : sOther->fBounds.fRight > basePt.fX) {
1867 return true;
1868 }
1869 } while (fTs[++tIndex].fT == 0);
1870 }
1871 if (fPts[fVerb].fX == basePt.fX) {
1872 tIndex = fTs.count() - 1;
1873 do {
1874 const Span& eSpan = fTs[tIndex];
1875 const Segment* eOther = eSpan.fOther;
1876 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1877 continue;
1878 }
1879 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1880 : eOther->fBounds.fRight > basePt.fX) {
1881 return true;
1882 }
1883 } while (fTs[--tIndex].fT == 1);
1884 }
1885 return false;
1886 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001887
caryclark@google.com18063442012-07-25 12:05:18 +00001888 bool decrementSpan(Span* span) {
1889 SkASSERT(span->fWindValue > 0);
1890 if (--(span->fWindValue) == 0) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001891 if (!span->fDone) {
1892 span->fDone = true;
1893 ++fDoneSpans;
1894 }
caryclark@google.com18063442012-07-25 12:05:18 +00001895 return true;
1896 }
1897 return false;
1898 }
1899
caryclark@google.com15fa1382012-05-07 20:49:36 +00001900 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001901 SkASSERT(fDoneSpans <= fTs.count());
1902 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001903 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001904
caryclark@google.comf839c032012-10-26 21:03:50 +00001905 bool done(int min) const {
1906 return fTs[min].fDone;
1907 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001908
caryclark@google.com47580692012-07-23 12:14:49 +00001909 bool done(const Angle& angle) const {
caryclark@google.comf839c032012-10-26 21:03:50 +00001910 return done(SkMin32(angle.start(), angle.end()));
caryclark@google.com47580692012-07-23 12:14:49 +00001911 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001912
caryclark@google.com235f56a2012-09-14 14:19:30 +00001913 Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
caryclark@google.com31143cf2012-11-09 22:14:19 +00001914 int& nextStart, int& nextEnd, int& winding, int& oppWinding,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001915 int& spanWinding, int& oppSpanWinding, bool& unsortable, ShapeOp op,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001916 const int aXorMask, const int bXorMask) {
1917 const int startIndex = nextStart;
1918 const int endIndex = nextEnd;
1919 int outerWinding = winding;
1920 int innerWinding = winding + spanWinding;
1921 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00001922 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d oppWinding=%d\n",
1923 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001924 #endif
1925 if (useInnerWinding(outerWinding, innerWinding)) {
1926 outerWinding = innerWinding;
1927 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001928 int outerOppWinding = oppWinding;
1929 if (oppSpanWinding) {
1930 int innerOppWinding = oppWinding + oppSpanWinding;
1931 if (useInnerWinding(oppWinding, innerOppWinding)) {
1932 outerOppWinding = innerOppWinding;
1933 }
1934 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001935 SkASSERT(startIndex != endIndex);
1936 int count = fTs.count();
1937 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1938 : startIndex > 0);
1939 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001940 int end = nextExactSpan(startIndex, step);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001941 SkASSERT(end >= 0);
1942 Span* endSpan = &fTs[end];
1943 Segment* other;
1944 if (isSimple(end)) {
1945 // mark the smaller of startIndex, endIndex done, and all adjacent
1946 // spans with the same T value (but not 'other' spans)
1947 #if DEBUG_WINDING
1948 SkDebugf("%s simple\n", __FUNCTION__);
1949 #endif
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001950 markDone(SkMin32(startIndex, endIndex), outerWinding, outerOppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001951 other = endSpan->fOther;
1952 nextStart = endSpan->fOtherIndex;
1953 double startT = other->fTs[nextStart].fT;
1954 nextEnd = nextStart;
1955 do {
1956 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001957 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00001958 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001959 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1960 return other;
1961 }
1962 // more than one viable candidate -- measure angles to find best
1963 SkTDArray<Angle> angles;
1964 SkASSERT(startIndex - endIndex != 0);
1965 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1966 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001967 buildAngles(end, angles, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001968 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001969 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001970 int angleCount = angles.count();
1971 int firstIndex = findStartingEdge(sorted, startIndex, end);
1972 SkASSERT(firstIndex >= 0);
1973 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001974 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001975 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001976 if (!sortable) {
1977 unsortable = true;
1978 return NULL;
1979 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001980 SkASSERT(sorted[firstIndex]->segment() == this);
1981 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001982 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
1983 sorted[firstIndex]->sign());
caryclark@google.com235f56a2012-09-14 14:19:30 +00001984 #endif
1985 int aSumWinding = winding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001986 int bSumWinding = oppWinding;
1987 bool angleIsOp = sorted[firstIndex]->segment()->operand() ^ operand();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001988 const Angle* firstAngle = sorted[firstIndex];
1989 int angleSpan = spanSign(firstAngle);
1990 int oppoSign = oppSign(firstAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001991 if (angleIsOp) {
1992 bSumWinding -= angleSpan;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001993 aSumWinding -= oppoSign;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001994 } else {
1995 aSumWinding -= angleSpan;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001996 bSumWinding -= oppoSign;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001997 }
1998 int nextIndex = firstIndex + 1;
1999 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2000 const Angle* foundAngle = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002001 bool foundDone = false;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002002#define TWO_CHANNEL_DONE 0
2003#if TWO_CHANNEL_DONE
caryclark@google.com235f56a2012-09-14 14:19:30 +00002004 bool foundDone2 = false;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002005#define FOUND_DONE2 foundDone2
2006#else
2007#define FOUND_DONE2 foundDone
2008#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002009 // iterate through the angle, and compute everyone's winding
caryclark@google.com31143cf2012-11-09 22:14:19 +00002010 bool aAltFlipped = false;
2011 bool bAltFlipped = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002012 bool foundFlipped = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002013 int foundSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002014 int foundOppWinding = SK_MinS32;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002015 Segment* nextSegment;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002016 int aLastNonZeroSum = winding;
2017 int bLastNonZeroSum = oppWinding;
2018 bool foundOpp;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002019 do {
2020 if (nextIndex == angleCount) {
2021 nextIndex = 0;
2022 }
2023 const Angle* nextAngle = sorted[nextIndex];
2024 nextSegment = nextAngle->segment();
caryclark@google.comf839c032012-10-26 21:03:50 +00002025 bool nextDone = nextSegment->done(*nextAngle);
2026 bool nextTiny = nextSegment->tiny(*nextAngle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002027 angleIsOp = nextSegment->operand() ^ operand();
2028 int deltaSum = nextSegment->spanSign(nextAngle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002029 int oppDeltaSum = nextSegment->oppSign(nextAngle);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002030 int maxWinding, xorMask, sumWinding, oMaxWinding, oSumWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002031 bool otherNonZero, altFlipped, otherCoin;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002032 if (angleIsOp) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002033 maxWinding = bSumWinding;
2034 if (bSumWinding) {
2035 bLastNonZeroSum = bSumWinding;
2036 }
2037 bSumWinding -= deltaSum;
2038 sumWinding = bSumWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002039 otherNonZero = aSumWinding & aXorMask;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002040 xorMask = bXorMask;
2041 bAltFlipped ^= bLastNonZeroSum * bSumWinding < 0; // flip if different signs
2042 altFlipped = bAltFlipped;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002043 oMaxWinding = aSumWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002044 aSumWinding -= oppDeltaSum;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002045 oSumWinding = aSumWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002046 otherCoin = aSumWinding & aXorMask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002047 } else {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002048 maxWinding = aSumWinding;
2049 if (aSumWinding) {
2050 aLastNonZeroSum = aSumWinding;
2051 }
2052 aSumWinding -= deltaSum;
2053 sumWinding = aSumWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002054 otherNonZero = bSumWinding & bXorMask;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002055 xorMask = aXorMask;
2056 aAltFlipped ^= aLastNonZeroSum * aSumWinding < 0; // flip if different signs
2057 altFlipped = aAltFlipped;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002058 oMaxWinding = bSumWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002059 bSumWinding -= oppDeltaSum;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002060 oSumWinding = bSumWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002061 otherCoin = bSumWinding & bXorMask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002062 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002063 if (oppDeltaSum && useInnerWinding(oMaxWinding, oSumWinding)) {
2064 oMaxWinding = oSumWinding;
2065 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002066 bool opIsActive = activeOp(nextSegment->operand(), otherNonZero, otherCoin, op);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002067 if (!(sumWinding & xorMask)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002068 if (!active) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002069 markAndChaseDone(startIndex, endIndex, outerWinding, outerOppWinding);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002070 nextSegment->markAndChaseWinding(nextAngle, maxWinding, oMaxWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002071 #if DEBUG_WINDING
2072 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
2073 #endif
2074 return NULL;
2075 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002076 if (opIsActive && (!foundAngle || foundDone)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002077 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002078 foundDone = nextDone && !nextTiny;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002079 foundFlipped = altFlipped;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002080 foundSum = 0;
2081 foundOpp = angleIsOp;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002082 foundOppWinding = oMaxWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002083 }
2084 continue;
2085 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002086 if (opIsActive && !(maxWinding & xorMask) && (!foundAngle || FOUND_DONE2)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002087 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00002088 if (foundAngle && FOUND_DONE2) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002089 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
2090 }
2091 #endif
2092 foundAngle = nextAngle;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002093 FOUND_DONE2 = nextDone && !nextTiny;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002094 foundFlipped = altFlipped;
2095 foundSum = sumWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002096 foundOpp = angleIsOp;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002097 foundOppWinding = oMaxWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002098 }
2099 if (nextSegment->done()) {
2100 continue;
2101 }
2102 // if the winding is non-zero, nextAngle does not connect to
2103 // current chain. If we haven't done so already, mark the angle
2104 // as done, record the winding value, and mark connected unambiguous
2105 // segments as well.
2106 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
2107 if (useInnerWinding(maxWinding, sumWinding)) {
2108 maxWinding = sumWinding;
2109 }
2110 Span* last;
2111 if (foundAngle) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002112 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding, oMaxWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002113 } else {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002114 last = nextSegment->markAndChaseDone(nextAngle, maxWinding, oMaxWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002115 }
2116 if (last) {
2117 *chase.append() = last;
2118 }
2119 }
2120 } while (++nextIndex != lastIndex);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002121 markDone(SkMin32(startIndex, endIndex), outerWinding, outerOppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002122 if (!foundAngle) {
2123 return NULL;
2124 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002125 #if DEBUG_WINDING
2126 int oldSpanSign = spanSign(nextStart, nextEnd);
2127 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002128 nextStart = foundAngle->start();
2129 nextEnd = foundAngle->end();
2130 nextSegment = foundAngle->segment();
2131 int flipped = foundFlipped ? -1 : 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002132 int minStartEnd = SkMin32(nextStart, nextEnd);
2133 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(minStartEnd);
2134 oppSpanWinding = SkSign32(oppSpanWinding) * flipped * nextSegment->oppValue(minStartEnd);
2135
caryclark@google.com235f56a2012-09-14 14:19:30 +00002136 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00002137 SkDebugf("%s foundFlipped=%d spanWinding=%d oldSpanSign=%d spanSign=%d\n",
2138 __FUNCTION__, foundFlipped, spanWinding, oldSpanSign,
2139 nextSegment->spanSign(foundAngle));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002140 SkDebugf("%s foundOpp=%d oppWinding=%d oppSpanWinding=%d foundOppWinding=%d winding=%d"
2141 " foundSum=", __FUNCTION__, foundOpp, oppWinding, oppSpanWinding, foundOppWinding,
2142 winding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002143 if (foundSum == SK_MinS32) {
2144 SkDebugf("?");
2145 } else {
2146 SkDebugf("%d", foundSum);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002147 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002148 SkDebugf("\n");
2149 #endif
2150 if (oppWinding != foundOppWinding) {
2151 oppWinding = foundOppWinding;
2152 if (foundOpp) {
2153 SkASSERT(foundSum != SK_MinS32);
2154 winding = foundSum;
2155 spanWinding = nextSegment->spanSign(foundAngle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002156 oppSpanWinding = nextSegment->oppSign(foundAngle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002157 }
2158 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002159 return nextSegment;
2160 }
caryclark@google.com47580692012-07-23 12:14:49 +00002161
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002162 // so the span needs to contain the pairing info found here
2163 // this should include the winding computed for the edge, and
2164 // what edge it connects to, and whether it is discarded
2165 // (maybe discarded == abs(winding) > 1) ?
2166 // only need derivatives for duration of sorting, add a new struct
2167 // for pairings, remove extra spans that have zero length and
2168 // reference an unused other
2169 // for coincident, the last span on the other may be marked done
2170 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002171
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002172 // if loop is exhausted, contour may be closed.
2173 // FIXME: pass in close point so we can check for closure
2174
2175 // given a segment, and a sense of where 'inside' is, return the next
2176 // segment. If this segment has an intersection, or ends in multiple
2177 // segments, find the mate that continues the outside.
2178 // note that if there are multiples, but no coincidence, we can limit
2179 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002180
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002181 // mark found segments as done
2182
caryclark@google.com15fa1382012-05-07 20:49:36 +00002183 // start is the index of the beginning T of this edge
2184 // it is guaranteed to have an end which describes a non-zero length (?)
2185 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002186 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002187 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2188 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002189 const int startIndex = nextStart;
2190 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002191 int outerWinding = winding;
2192 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002193 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002194 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2195 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002196 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002197 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002198 outerWinding = innerWinding;
2199 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002200 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002201 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002202 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2203 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002204 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002205 int end = nextExactSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002206 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002207 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002208 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002209 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002210 // mark the smaller of startIndex, endIndex done, and all adjacent
2211 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002212 #if DEBUG_WINDING
2213 SkDebugf("%s simple\n", __FUNCTION__);
2214 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002215 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002216 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002217 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002218 double startT = other->fTs[nextStart].fT;
2219 nextEnd = nextStart;
2220 do {
2221 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002222 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002223 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00002224 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002225 return other;
2226 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002227 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002228 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002229 SkASSERT(startIndex - endIndex != 0);
2230 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002231 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002232 buildAngles(end, angles, false);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002233 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002234 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002235 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002236 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002237 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002238 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002239 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002240 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002241 if (!sortable) {
2242 unsortable = true;
2243 return NULL;
2244 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002245 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002246 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002247 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002248 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002249 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002250 int nextIndex = firstIndex + 1;
2251 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2252 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002253 // FIXME: found done logic probably fails if there are more than 4
2254 // sorted angles. It should bias towards the first and last undone
2255 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002256 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002257 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002258 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002259 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002260 bool altFlipped = false;
2261 bool foundFlipped = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002262 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002263 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002264 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002265 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002266 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002267 nextIndex = 0;
2268 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002269 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002270 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002271 if (sumWinding) {
2272 lastNonZeroSum = sumWinding;
2273 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002274 nextSegment = nextAngle->segment();
caryclark@google.comf839c032012-10-26 21:03:50 +00002275 bool nextDone = nextSegment->done(*nextAngle);
2276 bool nextTiny = nextSegment->tiny(*nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002277 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002278 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002279 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002280 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002281 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2282 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002283 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002284 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002285 if (!active) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002286 // FIXME: shouldn't this call mark and chase done ?
caryclark@google.com59823f72012-08-09 18:17:47 +00002287 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002288 // FIXME: shouldn't this call mark and chase winding ?
caryclark@google.com47580692012-07-23 12:14:49 +00002289 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002290 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002291 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002292 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002293 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002294 return NULL;
2295 }
caryclark@google.com47580692012-07-23 12:14:49 +00002296 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002297 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002298 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002299 foundFlipped = altFlipped;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002300 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002301 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002302 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002303
caryclark@google.com24bec792012-08-20 12:43:57 +00002304 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002305 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002306 if (foundAngle && foundDone2) {
2307 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002308 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002309 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002310 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002311 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002312 foundFlipped = altFlipped;
2313 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002314 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002315 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002316 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002317 }
2318 // if the winding is non-zero, nextAngle does not connect to
2319 // current chain. If we haven't done so already, mark the angle
2320 // as done, record the winding value, and mark connected unambiguous
2321 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002322 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002323 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002324 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002325 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002326 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002327 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002328 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002329 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002330 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002331 }
2332 if (last) {
2333 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002334 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002335 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002336 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002337 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002338 if (!foundAngle) {
2339 return NULL;
2340 }
2341 nextStart = foundAngle->start();
2342 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002343 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002344 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002345 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2346 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002347 if (winding) {
2348 #if DEBUG_WINDING
2349 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2350 if (foundSum == SK_MinS32) {
2351 SkDebugf("?");
2352 } else {
2353 SkDebugf("%d", foundSum);
2354 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002355 SkDebugf("\n");
2356 #endif
2357 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002358 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002359 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002360 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002361 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002362 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002363 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002364
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002365 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002366 const int startIndex = nextStart;
2367 const int endIndex = nextEnd;
2368 SkASSERT(startIndex != endIndex);
2369 int count = fTs.count();
2370 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2371 : startIndex > 0);
2372 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002373 int end = nextExactSpan(startIndex, step);
caryclark@google.com24bec792012-08-20 12:43:57 +00002374 SkASSERT(end >= 0);
2375 Span* endSpan = &fTs[end];
2376 Segment* other;
2377 markDone(SkMin32(startIndex, endIndex), 1);
2378 if (isSimple(end)) {
2379 #if DEBUG_WINDING
2380 SkDebugf("%s simple\n", __FUNCTION__);
2381 #endif
2382 other = endSpan->fOther;
2383 nextStart = endSpan->fOtherIndex;
2384 double startT = other->fTs[nextStart].fT;
2385 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002386 if ((approximately_less_than_zero(startT) && step < 0)
2387 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002388 step = -step;
2389 SkDEBUGCODE(firstLoop = false;)
2390 }
2391 do {
2392 nextEnd = nextStart;
2393 do {
2394 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002395 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002396 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002397 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2398 break;
2399 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002400 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002401 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002402 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002403 SkDEBUGCODE(firstLoop = false;)
2404 step = -step;
2405 } while (true);
2406 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2407 return other;
2408 }
2409 SkTDArray<Angle> angles;
2410 SkASSERT(startIndex - endIndex != 0);
2411 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2412 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002413 buildAngles(end, angles, false);
caryclark@google.com24bec792012-08-20 12:43:57 +00002414 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002415 bool sortable = SortAngles(angles, sorted);
caryclark@google.com24bec792012-08-20 12:43:57 +00002416 int angleCount = angles.count();
2417 int firstIndex = findStartingEdge(sorted, startIndex, end);
2418 SkASSERT(firstIndex >= 0);
2419 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002420 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002421 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002422 if (!sortable) {
2423 unsortable = true;
2424 return NULL;
2425 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002426 SkASSERT(sorted[firstIndex]->segment() == this);
2427 int nextIndex = firstIndex + 1;
2428 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2429 const Angle* nextAngle;
2430 Segment* nextSegment;
2431 do {
2432 if (nextIndex == angleCount) {
2433 nextIndex = 0;
2434 }
2435 nextAngle = sorted[nextIndex];
2436 nextSegment = nextAngle->segment();
2437 if (!nextSegment->done(*nextAngle)) {
2438 break;
2439 }
2440 if (++nextIndex == lastIndex) {
2441 return NULL;
2442 }
2443 } while (true);
2444 nextStart = nextAngle->start();
2445 nextEnd = nextAngle->end();
2446 return nextSegment;
2447 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002448
2449 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2450 int angleCount = sorted.count();
2451 int firstIndex = -1;
2452 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2453 const Angle* angle = sorted[angleIndex];
2454 if (angle->segment() == this && angle->start() == end &&
2455 angle->end() == start) {
2456 firstIndex = angleIndex;
2457 break;
2458 }
2459 }
2460 return firstIndex;
2461 }
2462
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002463 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002464 void findTooCloseToCall(bool thisXor, bool otherXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002465 int count = fTs.count();
2466 if (count < 3) { // require t=0, x, 1 at minimum
2467 return;
2468 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002469 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002470 int moCount;
2471 Span* match;
2472 Segment* mOther;
2473 do {
2474 match = &fTs[matchIndex];
2475 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002476 // FIXME: allow quads, cubics to be near coincident?
2477 if (mOther->fVerb == SkPath::kLine_Verb) {
2478 moCount = mOther->fTs.count();
2479 if (moCount >= 3) {
2480 break;
2481 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002482 }
2483 if (++matchIndex >= count) {
2484 return;
2485 }
2486 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002487 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002488 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002489 // look for a pair of nearby T values that map to the same (x,y) value
2490 // if found, see if the pair of other segments share a common point. If
2491 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002492 for (int index = matchIndex + 1; index < count; ++index) {
2493 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002494 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002495 continue;
2496 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002497 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002498 if (tOther->fVerb != SkPath::kLine_Verb) {
2499 continue; // FIXME: allow quads, cubics to be near coincident?
2500 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002501 int toCount = tOther->fTs.count();
2502 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002503 continue;
2504 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002505 const SkPoint* testPt = &xyAtT(test);
2506 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002507 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002508 moCount = toCount;
2509 match = test;
2510 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002511 matchPt = testPt;
2512 continue;
2513 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002514 int moStart = -1;
2515 int moEnd = -1;
2516 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002517 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002518 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002519 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002520 continue;
2521 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002522 if (moSpan.fOther == this) {
2523 if (moSpan.fOtherT == match->fT) {
2524 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002525 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002526 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002527 continue;
2528 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002529 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002530 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2531 moStart = -1;
2532 break;
2533 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002534 SkASSERT(moEnd == -1);
2535 moEnd = moIndex;
2536 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002537 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002538 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002539 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002540 continue;
2541 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002542 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002543 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002544 continue;
2545 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002546 int toStart = -1;
2547 int toEnd = -1;
2548 double toStartT, toEndT;
2549 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2550 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002551 if (toSpan.fDone) {
2552 continue;
2553 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002554 if (toSpan.fOther == this) {
2555 if (toSpan.fOtherT == test->fT) {
2556 toStart = toIndex;
2557 toStartT = toSpan.fT;
2558 }
2559 continue;
2560 }
2561 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002562 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2563 moStart = -1;
2564 break;
2565 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002566 SkASSERT(toEnd == -1);
2567 toEnd = toIndex;
2568 toEndT = toSpan.fT;
2569 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002570 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002571 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2572 if (toStart <= 0 || toEnd <= 0) {
2573 continue;
2574 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002575 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002576 continue;
2577 }
2578 // test to see if the segment between there and here is linear
2579 if (!mOther->isLinear(moStart, moEnd)
2580 || !tOther->isLinear(toStart, toEnd)) {
2581 continue;
2582 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002583 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002584 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002585 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002586 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002587 // FIXME: this is bogus for multiple ops
2588 // the xorMask needs to be accumulated from the union of the two
2589 // edges -- which means that the segment must have its own copy of the mask
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002590 mOther->addTCoincident(thisXor, otherXor,
2591 moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002592 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002593 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002594 }
2595
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002596 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002597 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002598 // a) mark spans with either end unsortable as done, or
2599 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2600 // when encountering an unsortable span
2601
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002602 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2603 // and use more concise logic like the old edge walker code?
2604 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002605 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002606 // iterate through T intersections and return topmost
2607 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002608 SkASSERT(!done());
2609 int firstT;
2610 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002611 SkPoint topPt;
2612 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002613 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002614 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002615 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002616 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002617 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002618 const Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00002619 if (span.fUnsortableStart | lastUnsortable) {
2620 goto next;
2621 }
2622 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002623 const SkPoint& intercept = xyAtT(&span);
2624 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2625 && topPt.fX > intercept.fX)) {
2626 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002627 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002628 } else if (topPt == intercept) {
2629 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002630 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002631 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002632 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002633 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002634 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002635 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002636 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002637 int step = 1;
2638 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002639 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002640 step = -1;
2641 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002642 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002643 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002644 // if the topmost T is not on end, or is three-way or more, find left
2645 // look for left-ness from tLeft to firstT (matching y of other)
2646 SkTDArray<Angle> angles;
2647 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002648 addTwoAngles(end, firstT, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002649 buildAngles(firstT, angles, true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002650 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002651 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002652 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002653 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002654 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00002655 if (!sortable) {
2656 return NULL;
2657 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002658 // skip edges that have already been processed
2659 firstT = -1;
2660 Segment* leftSegment;
2661 do {
2662 const Angle* angle = sorted[++firstT];
caryclark@google.comf839c032012-10-26 21:03:50 +00002663 SkASSERT(!angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002664 leftSegment = angle->segment();
2665 tIndex = angle->end();
2666 endIndex = angle->start();
2667 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002668 return leftSegment;
2669 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002670
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002671 // FIXME: not crazy about this
2672 // when the intersections are performed, the other index is into an
2673 // incomplete array. as the array grows, the indices become incorrect
2674 // while the following fixes the indices up again, it isn't smart about
2675 // skipping segments whose indices are already correct
2676 // assuming we leave the code that wrote the index in the first place
2677 void fixOtherTIndex() {
2678 int iCount = fTs.count();
2679 for (int i = 0; i < iCount; ++i) {
2680 Span& iSpan = fTs[i];
2681 double oT = iSpan.fOtherT;
2682 Segment* other = iSpan.fOther;
2683 int oCount = other->fTs.count();
2684 for (int o = 0; o < oCount; ++o) {
2685 Span& oSpan = other->fTs[o];
2686 if (oT == oSpan.fT && this == oSpan.fOther) {
2687 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002688 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002689 }
2690 }
2691 }
2692 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002693
caryclark@google.com495f8e42012-05-31 13:13:11 +00002694 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002695 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002696 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002697 SkASSERT(end >= 0);
2698 if (multipleSpans(end)) {
2699 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002700 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002701 const Span& endSpan = fTs[end];
2702 Segment* other = endSpan.fOther;
2703 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002704 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002705 Span* last = other->innerChaseDone(index, step, winding);
2706 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002707 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002708 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002709
caryclark@google.com31143cf2012-11-09 22:14:19 +00002710 Span* innerChaseDone(int index, int step, int winding, int oppWinding) {
2711 int end = nextExactSpan(index, step);
2712 SkASSERT(end >= 0);
2713 if (multipleSpans(end)) {
2714 return &fTs[end];
2715 }
2716 const Span& endSpan = fTs[end];
2717 Segment* other = endSpan.fOther;
2718 index = endSpan.fOtherIndex;
2719 int otherEnd = other->nextExactSpan(index, step);
2720 Span* last = other->innerChaseDone(index, step, winding, oppWinding);
2721 other->markDone(SkMin32(index, otherEnd), winding, oppWinding);
2722 return last;
2723 }
2724
2725
caryclark@google.com59823f72012-08-09 18:17:47 +00002726 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002727 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002728 SkASSERT(end >= 0);
2729 if (multipleSpans(end)) {
2730 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002731 }
2732 const Span& endSpan = fTs[end];
2733 Segment* other = endSpan.fOther;
2734 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002735 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002736 int min = SkMin32(index, otherEnd);
2737 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002738 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002739 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002740 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002741 Span* last = other->innerChaseWinding(index, step, winding);
2742 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002743 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002744 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002745
caryclark@google.com31143cf2012-11-09 22:14:19 +00002746 Span* innerChaseWinding(int index, int step, int winding, int oppWinding) {
2747 int end = nextExactSpan(index, step);
2748 SkASSERT(end >= 0);
2749 if (multipleSpans(end)) {
2750 return &fTs[end];
2751 }
2752 const Span& endSpan = fTs[end];
2753 Segment* other = endSpan.fOther;
2754 index = endSpan.fOtherIndex;
2755 int otherEnd = other->nextExactSpan(index, step);
2756 int min = SkMin32(index, otherEnd);
2757 if (other->fTs[min].fWindSum != SK_MinS32) {
2758 SkASSERT(other->fTs[min].fWindSum == winding);
2759 return NULL;
2760 }
2761 Span* last = other->innerChaseWinding(index, step, winding, oppWinding);
2762 other->markWinding(min, winding, oppWinding);
2763 return last;
2764 }
2765
caryclark@google.com235f56a2012-09-14 14:19:30 +00002766 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2767 fDoneSpans = 0;
2768 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002769 fPts = pts;
2770 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002771 }
2772
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002773 bool intersected() const {
2774 return fTs.count() > 0;
2775 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002776
2777 bool isConnected(int startIndex, int endIndex) const {
2778 return fTs[startIndex].fWindSum != SK_MinS32
2779 || fTs[endIndex].fWindSum != SK_MinS32;
2780 }
2781
caryclark@google.com235f56a2012-09-14 14:19:30 +00002782 bool isHorizontal() const {
2783 return fBounds.fTop == fBounds.fBottom;
2784 }
2785
caryclark@google.com15fa1382012-05-07 20:49:36 +00002786 bool isLinear(int start, int end) const {
2787 if (fVerb == SkPath::kLine_Verb) {
2788 return true;
2789 }
2790 if (fVerb == SkPath::kQuad_Verb) {
2791 SkPoint qPart[3];
2792 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2793 return QuadIsLinear(qPart);
2794 } else {
2795 SkASSERT(fVerb == SkPath::kCubic_Verb);
2796 SkPoint cPart[4];
2797 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2798 return CubicIsLinear(cPart);
2799 }
2800 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002801
2802 // OPTIMIZE: successive calls could start were the last leaves off
2803 // or calls could specialize to walk forwards or backwards
2804 bool isMissing(double startT) const {
2805 size_t tCount = fTs.count();
2806 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002807 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002808 return false;
2809 }
2810 }
2811 return true;
2812 }
2813
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002814 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002815 int count = fTs.count();
2816 if (count == 2) {
2817 return true;
2818 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002819 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002820 if (approximately_less_than_zero(t)) {
2821 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002822 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002823 if (approximately_greater_than_one(t)) {
2824 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002825 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002826 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002827 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002828
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002829 bool isVertical() const {
2830 return fBounds.fLeft == fBounds.fRight;
2831 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002832
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002833 SkScalar leftMost(int start, int end) const {
2834 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2835 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002836
caryclark@google.com495f8e42012-05-31 13:13:11 +00002837 // this span is excluded by the winding rule -- chase the ends
2838 // as long as they are unambiguous to mark connections as done
2839 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002840 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002841 int index = angle->start();
2842 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002843 return markAndChaseDone(index, endIndex, winding);
2844 }
2845
2846 Span* markAndChaseDone(const Angle* angle, int winding, int oppWinding) {
2847 int index = angle->start();
2848 int endIndex = angle->end();
2849 return markAndChaseDone(index, endIndex, winding, oppWinding);
2850 }
2851
2852 Span* markAndChaseDone(int index, int endIndex, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002853 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002854 Span* last = innerChaseDone(index, step, winding);
2855 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002856 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002857 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002858
caryclark@google.com31143cf2012-11-09 22:14:19 +00002859 Span* markAndChaseDone(int index, int endIndex, int winding, int oppWinding) {
2860 int step = SkSign32(endIndex - index);
2861 Span* last = innerChaseDone(index, step, winding, oppWinding);
2862 markDone(SkMin32(index, endIndex), winding, oppWinding);
2863 return last;
2864 }
2865
caryclark@google.com59823f72012-08-09 18:17:47 +00002866 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002867 int index = angle->start();
2868 int endIndex = angle->end();
2869 int min = SkMin32(index, endIndex);
2870 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002871 Span* last = innerChaseWinding(index, step, winding);
2872 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002873 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002874 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002875
caryclark@google.com31143cf2012-11-09 22:14:19 +00002876 Span* markAndChaseWinding(const Angle* angle, int winding, int oppWinding) {
2877 int index = angle->start();
2878 int endIndex = angle->end();
2879 int min = SkMin32(index, endIndex);
2880 int step = SkSign32(endIndex - index);
2881 Span* last = innerChaseWinding(index, step, winding, oppWinding);
2882 markWinding(min, winding, oppWinding);
2883 return last;
2884 }
2885
caryclark@google.com495f8e42012-05-31 13:13:11 +00002886 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002887 // This may be called when the segment is already marked done. While this
2888 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002889 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002890 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002891 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002892 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002893 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002894 double referenceT = fTs[index].fT;
2895 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002896 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2897 markOneDone(__FUNCTION__, lesser, winding);
2898 }
2899 do {
2900 markOneDone(__FUNCTION__, index, winding);
2901 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002902 }
2903
2904 void markDone(int index, int winding, int oppWinding) {
2905 // SkASSERT(!done());
2906 SkASSERT(winding);
2907 double referenceT = fTs[index].fT;
2908 int lesser = index;
2909 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2910 markOneDone(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002911 }
2912 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002913 markOneDone(__FUNCTION__, index, winding, oppWinding);
2914 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002915 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002916
caryclark@google.com24bec792012-08-20 12:43:57 +00002917 void markOneDone(const char* funName, int tIndex, int winding) {
2918 Span* span = markOneWinding(funName, tIndex, winding);
2919 if (!span) {
2920 return;
2921 }
2922 span->fDone = true;
2923 fDoneSpans++;
2924 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002925
caryclark@google.com31143cf2012-11-09 22:14:19 +00002926 void markOneDone(const char* funName, int tIndex, int winding, int oppWinding) {
2927 Span* span = markOneWinding(funName, tIndex, winding, oppWinding);
2928 if (!span) {
2929 return;
2930 }
2931 span->fDone = true;
2932 fDoneSpans++;
2933 }
2934
caryclark@google.com24bec792012-08-20 12:43:57 +00002935 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2936 Span& span = fTs[tIndex];
2937 if (span.fDone) {
2938 return NULL;
2939 }
2940 #if DEBUG_MARK_DONE
2941 debugShowNewWinding(funName, span, winding);
2942 #endif
2943 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002944 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002945 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002946 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002947 span.fWindSum = winding;
2948 return &span;
2949 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002950
caryclark@google.com31143cf2012-11-09 22:14:19 +00002951 Span* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding) {
2952 Span& span = fTs[tIndex];
2953 if (span.fDone) {
2954 return NULL;
2955 }
2956 #if DEBUG_MARK_DONE
2957 debugShowNewWinding(funName, span, winding, oppWinding);
2958 #endif
2959 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
2960 #ifdef SK_DEBUG
2961 SkASSERT(abs(winding) <= gDebugMaxWindSum);
2962 #endif
2963 span.fWindSum = winding;
2964 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
2965 #ifdef SK_DEBUG
2966 SkASSERT(abs(oppWinding) <= gDebugMaxWindSum);
2967 #endif
2968 span.fOppSum = oppWinding;
2969 return &span;
2970 }
2971
caryclark@google.comf839c032012-10-26 21:03:50 +00002972 // note that just because a span has one end that is unsortable, that's
2973 // not enough to mark it done. The other end may be sortable, allowing the
2974 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002975 void markUnsortable(int start, int end) {
2976 Span* span = &fTs[start];
2977 if (start < end) {
2978 span->fUnsortableStart = true;
2979 } else {
2980 --span;
2981 span->fUnsortableEnd = true;
2982 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002983 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002984 return;
2985 }
2986 span->fDone = true;
2987 fDoneSpans++;
2988 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002989
caryclark@google.com59823f72012-08-09 18:17:47 +00002990 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002991 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002992 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002993 double referenceT = fTs[index].fT;
2994 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002995 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2996 markOneWinding(__FUNCTION__, lesser, winding);
2997 }
2998 do {
2999 markOneWinding(__FUNCTION__, index, winding);
3000 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00003001 }
3002
3003 void markWinding(int index, int winding, int oppWinding) {
3004 // SkASSERT(!done());
3005 SkASSERT(winding);
3006 double referenceT = fTs[index].fT;
3007 int lesser = index;
3008 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3009 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003010 }
3011 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003012 markOneWinding(__FUNCTION__, index, winding, oppWinding);
3013 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003014 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003015
caryclark@google.com2ddff932012-08-07 21:25:27 +00003016 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003017 int nextDoorWind = SK_MaxS32;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003018 int nextOppWind = SK_MaxS32;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003019 if (tIndex > 0) {
3020 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003021 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003022 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003023 nextOppWind = below.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003024 }
3025 }
3026 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3027 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003028 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003029 nextDoorWind = above.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003030 nextOppWind = above.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003031 }
3032 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003033 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
3034 const Span& below = fTs[tIndex - 1];
3035 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003036 nextOppWind = below.fOppValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003037 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003038 if (nextDoorWind != SK_MaxS32) {
3039 Span& newSpan = fTs[tIndex];
3040 newSpan.fWindValue = nextDoorWind;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003041 newSpan.fOppValue = nextOppWind;
caryclark@google.comf839c032012-10-26 21:03:50 +00003042 if (!nextDoorWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003043 newSpan.fDone = true;
3044 ++fDoneSpans;
3045 }
3046 }
3047 }
3048
caryclark@google.com9764cc62012-07-12 19:29:45 +00003049 // return span if when chasing, two or more radiating spans are not done
3050 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
3051 // candidate and the remaining spans have windValue == 0 (canceled by
3052 // coincidence). The coincident edges could either be removed altogether,
3053 // or this code could be more complicated in detecting this case. Worth it?
3054 bool multipleSpans(int end) const {
3055 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003056 }
3057
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003058 // This has callers for two different situations: one establishes the end
3059 // of the current span, and one establishes the beginning of the next span
3060 // (thus the name). When this is looking for the end of the current span,
3061 // coincidence is found when the beginning Ts contain -step and the end
3062 // contains step. When it is looking for the beginning of the next, the
3063 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003064 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003065 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003066 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00003067 int count = fTs.count();
3068 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00003069 while (step > 0 ? ++to < count : --to >= 0) {
3070 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003071 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003072 continue;
3073 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003074 return to;
3075 }
3076 return -1;
3077 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00003078
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003079 // FIXME
3080 // this returns at any difference in T, vs. a preset minimum. It may be
3081 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003082 // OPTIMIZATION splitting this into separate loops for up/down steps
3083 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003084 int nextExactSpan(int from, int step) const {
3085 const Span& fromSpan = fTs[from];
3086 int count = fTs.count();
3087 int to = from;
3088 while (step > 0 ? ++to < count : --to >= 0) {
3089 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00003090 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003091 continue;
3092 }
3093 return to;
3094 }
3095 return -1;
3096 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003097
caryclark@google.com235f56a2012-09-14 14:19:30 +00003098 bool operand() const {
3099 return fOperand;
3100 }
3101
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003102 int oppSign(const Angle* angle) const {
3103 SkASSERT(angle->segment() == this);
3104 return oppSign(angle->start(), angle->end());
3105 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003106
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003107 int oppSign(int startIndex, int endIndex) const {
3108 int result = startIndex < endIndex ? -fTs[startIndex].fOppValue
3109 : fTs[endIndex].fOppValue;
3110#if DEBUG_WIND_BUMP
3111 SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
3112#endif
3113 return result;
3114 }
3115
caryclark@google.com31143cf2012-11-09 22:14:19 +00003116 int oppSum(int tIndex) const {
3117 return fTs[tIndex].fOppSum;
3118 }
3119
3120 int oppSum(const Angle* angle) const {
3121 int lesser = SkMin32(angle->start(), angle->end());
3122 return fTs[lesser].fOppSum;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003123 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003124
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003125 int oppValue(int tIndex) const {
3126 return fTs[tIndex].fOppValue;
3127 }
3128
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003129 const SkPoint* pts() const {
3130 return fPts;
3131 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003132
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003133 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003134 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003135 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
3136 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003137 }
3138
caryclark@google.comf839c032012-10-26 21:03:50 +00003139 // This marks all spans unsortable so that this info is available for early
3140 // exclusion in find top and others. This could be optimized to only mark
3141 // adjacent spans that unsortable. However, this makes it difficult to later
3142 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003143 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003144 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003145 int angleCount = angles.count();
3146 int angleIndex;
3147 angleList.setReserve(angleCount);
3148 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003149 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00003150 *angleList.append() = &angle;
3151 sortable &= !angle.unsortable();
3152 }
3153 if (sortable) {
3154 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3155 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3156 if (angles[angleIndex].unsortable()) {
3157 sortable = false;
3158 break;
3159 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003160 }
3161 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003162 if (!sortable) {
3163 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3164 Angle& angle = angles[angleIndex];
3165 angle.segment()->markUnsortable(angle.start(), angle.end());
3166 }
3167 }
3168 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003169 }
3170
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003171 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003172 const Span& span(int tIndex) const {
3173 return fTs[tIndex];
3174 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003175
caryclark@google.com235f56a2012-09-14 14:19:30 +00003176 int spanSign(const Angle* angle) const {
3177 SkASSERT(angle->segment() == this);
3178 return spanSign(angle->start(), angle->end());
3179 }
3180
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003181 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003182 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue
3183 : fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003184#if DEBUG_WIND_BUMP
3185 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3186#endif
3187 return result;
3188 }
3189
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003190 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003191 double t(int tIndex) const {
3192 return fTs[tIndex].fT;
3193 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003194
caryclark@google.comf839c032012-10-26 21:03:50 +00003195 bool tiny(const Angle& angle) const {
3196 int start = angle.start();
3197 int end = angle.end();
3198 const Span& mSpan = fTs[SkMin32(start, end)];
3199 return mSpan.fTiny;
3200 }
3201
caryclark@google.com18063442012-07-25 12:05:18 +00003202 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3203 double start) {
3204 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003205 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003206 *outsideTs.append() = end;
3207 *outsideTs.append() = start;
3208 }
3209 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003210
caryclark@google.com24bec792012-08-20 12:43:57 +00003211 void undoneSpan(int& start, int& end) {
3212 size_t tCount = fTs.count();
3213 size_t index;
3214 for (index = 0; index < tCount; ++index) {
3215 if (!fTs[index].fDone) {
3216 break;
3217 }
3218 }
3219 SkASSERT(index < tCount - 1);
3220 start = index;
3221 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003222 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003223 SkASSERT(index < tCount);
3224 SkASSERT(index < tCount);
3225 end = index;
3226 }
caryclark@google.com18063442012-07-25 12:05:18 +00003227
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003228 bool unsortable(int index) const {
3229 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3230 }
3231
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003232 void updatePts(const SkPoint pts[]) {
3233 fPts = pts;
3234 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003235
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003236 SkPath::Verb verb() const {
3237 return fVerb;
3238 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003239
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003240 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003241 return fTs[tIndex].fWindSum;
3242 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003243
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003244 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003245 int start = angle->start();
3246 int end = angle->end();
3247 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003248 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003249 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003250
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003251 int windValue(int tIndex) const {
3252 return fTs[tIndex].fWindValue;
3253 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003254
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003255 int windValue(const Angle* angle) const {
3256 int start = angle->start();
3257 int end = angle->end();
3258 int index = SkMin32(start, end);
3259 return windValue(index);
3260 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003261
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003262 SkScalar xAtT(const Span* span) const {
3263 return xyAtT(span).fX;
3264 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003265
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003266 const SkPoint& xyAtT(int index) const {
3267 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003268 }
3269
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003270 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003271 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003272 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003273 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003274 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003275 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003276 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003277 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003278 }
3279 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003280 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003281 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003282
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003283 SkScalar yAtT(int index) const {
3284 return yAtT(&fTs[index]);
3285 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003286
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003287 SkScalar yAtT(const Span* span) const {
3288 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003289 }
3290
caryclark@google.com6ec15262012-11-16 20:16:50 +00003291 void zeroSpan(Span* span) {
3292 SkASSERT(span->fWindValue > 0);
3293 span->fWindValue = 0;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003294 span->fOppValue = 0;
caryclark@google.com6ec15262012-11-16 20:16:50 +00003295 if (!span->fDone) {
3296 span->fDone = true;
3297 ++fDoneSpans;
3298 }
3299 }
3300
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003301#if DEBUG_DUMP
3302 void dump() const {
3303 const char className[] = "Segment";
3304 const int tab = 4;
3305 for (int i = 0; i < fTs.count(); ++i) {
3306 SkPoint out;
3307 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3308 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003309 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003310 tab + sizeof(className), className, fID,
3311 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003312 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003313 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003314 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003315 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003316 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003317 }
3318#endif
3319
caryclark@google.com47580692012-07-23 12:14:49 +00003320#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003321 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003322 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003323 for (int i = 0; i < fTs.count(); ++i) {
3324 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3325 return;
3326 }
3327 }
3328 SkASSERT(0);
3329 }
3330#endif
3331
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003332#if DEBUG_DUMP
3333 int debugID() const {
3334 return fID;
3335 }
3336#endif
3337
caryclark@google.com24bec792012-08-20 12:43:57 +00003338#if DEBUG_WINDING
3339 void debugShowSums() const {
3340 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3341 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3342 for (int i = 0; i < fTs.count(); ++i) {
3343 const Span& span = fTs[i];
3344 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3345 if (span.fWindSum == SK_MinS32) {
3346 SkDebugf("?");
3347 } else {
3348 SkDebugf("%d", span.fWindSum);
3349 }
3350 SkDebugf("]");
3351 }
3352 SkDebugf("\n");
3353 }
3354#endif
3355
caryclark@google.comcc905052012-07-25 20:59:42 +00003356#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003357 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003358 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00003359 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00003360 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00003361 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
3362 }
3363 SkDebugf("\n");
3364 }
3365#endif
3366
caryclark@google.com027de222012-07-12 12:52:50 +00003367#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003368 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003369 if (done()) {
3370 return;
3371 }
3372 for (int i = 0; i < fTs.count(); ++i) {
3373 if (fTs[i].fDone) {
3374 continue;
3375 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003376 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003377 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3378 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3379 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3380 }
3381 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003382 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003383 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003384 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003385 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3386 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3387 if (fTs[i].fWindSum == SK_MinS32) {
3388 SkDebugf("?");
3389 } else {
3390 SkDebugf("%d", fTs[i].fWindSum);
3391 }
3392 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003393 }
3394 }
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +00003395
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003396 // This isn't useful yet -- but leaving it in for now in case i think of something
3397 // to use it for
3398 void validateActiveSpans() const {
3399 if (done()) {
3400 return;
3401 }
3402 int tCount = fTs.count();
3403 for (int index = 0; index < tCount; ++index) {
3404 if (fTs[index].fDone) {
3405 continue;
3406 }
3407 // count number of connections which are not done
3408 int first = index;
3409 double baseT = fTs[index].fT;
3410 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3411 --first;
3412 }
3413 int last = index;
3414 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3415 ++last;
3416 }
3417 int connections = 0;
3418 connections += first > 0 && !fTs[first - 1].fDone;
3419 for (int test = first; test <= last; ++test) {
3420 connections += !fTs[test].fDone;
3421 const Segment* other = fTs[test].fOther;
3422 int oIndex = fTs[test].fOtherIndex;
3423 connections += !other->fTs[oIndex].fDone;
3424 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3425 }
3426 // SkASSERT(!(connections & 1));
3427 }
3428 }
caryclark@google.com027de222012-07-12 12:52:50 +00003429#endif
3430
caryclark@google.com0c803d02012-08-06 11:15:47 +00003431#if DEBUG_MARK_DONE
3432 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3433 const SkPoint& pt = xyAtT(&span);
3434 SkDebugf("%s id=%d", fun, fID);
3435 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3436 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3437 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3438 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003439 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3440 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3441 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3442 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003443 if (span.fWindSum == SK_MinS32) {
3444 SkDebugf("?");
3445 } else {
3446 SkDebugf("%d", span.fWindSum);
3447 }
3448 SkDebugf(" windValue=%d\n", span.fWindValue);
3449 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003450
3451 void debugShowNewWinding(const char* fun, const Span& span, int winding, int oppWinding) {
3452 const SkPoint& pt = xyAtT(&span);
3453 SkDebugf("%s id=%d", fun, fID);
3454 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3455 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3456 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3457 }
3458 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3459 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3460 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d newOppSum=%d oppSum=",
3461 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
3462 winding, oppWinding);
3463 if (span.fOppSum == SK_MinS32) {
3464 SkDebugf("?");
3465 } else {
3466 SkDebugf("%d", span.fOppSum);
3467 }
3468 SkDebugf(" windSum=");
3469 if (span.fWindSum == SK_MinS32) {
3470 SkDebugf("?");
3471 } else {
3472 SkDebugf("%d", span.fWindSum);
3473 }
3474 SkDebugf(" windValue=%d\n", span.fWindValue);
3475 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003476#endif
3477
caryclark@google.com47580692012-07-23 12:14:49 +00003478#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003479 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com31143cf2012-11-09 22:14:19 +00003480 const int contourWinding, const int oppContourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003481 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003482 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003483 int lastSum = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003484 int oppLastSum = oppContourWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003485 const Angle* firstAngle = angles[first];
3486 int windSum = lastSum - spanSign(firstAngle);
3487 int oppoSign = oppSign(firstAngle);
3488 int oppWindSum = oppLastSum - oppoSign;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003489 SkDebugf("%s %s contourWinding=%d oppContourWinding=%d sign=%d\n", fun, __FUNCTION__,
3490 contourWinding, oppContourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003491 int index = first;
3492 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003493 do {
3494 const Angle& angle = *angles[index];
3495 const Segment& segment = *angle.segment();
3496 int start = angle.start();
3497 int end = angle.end();
3498 const Span& sSpan = segment.fTs[start];
3499 const Span& eSpan = segment.fTs[end];
3500 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com31143cf2012-11-09 22:14:19 +00003501 bool opp = segment.fOperand ^ fOperand;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003502 if (!firstTime) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003503 oppoSign = segment.oppSign(&angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003504 if (opp) {
3505 oppLastSum = oppWindSum;
3506 oppWindSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003507 if (oppoSign) {
3508 lastSum = windSum;
3509 windSum -= oppoSign;
3510 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003511 } else {
3512 lastSum = windSum;
3513 windSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003514 if (oppoSign) {
3515 oppLastSum = oppWindSum;
3516 oppWindSum -= oppoSign;
3517 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003518 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00003519 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003520 SkDebugf("%s [%d] %sid=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00003521 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003522 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003523 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003524 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3525 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3526 mSpan.fWindValue);
3527 if (mSpan.fWindSum == SK_MinS32) {
3528 SkDebugf("?");
3529 } else {
3530 SkDebugf("%d", mSpan.fWindSum);
3531 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003532 int last, wind;
3533 if (opp) {
3534 last = oppLastSum;
3535 wind = oppWindSum;
3536 } else {
3537 last = lastSum;
3538 wind = windSum;
3539 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003540 if (!oppoSign) {
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003541 SkDebugf(" %d->%d (max=%d)", last, wind,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003542 useInnerWinding(last, wind) ? wind : last);
3543 } else {
3544 SkDebugf(" %d->%d (%d->%d)", last, wind, opp ? lastSum : oppLastSum,
3545 opp ? windSum : oppWindSum);
3546 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003547 SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003548#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003549 angle.debugShow(segment.xyAtT(&sSpan));
3550#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003551 ++index;
3552 if (index == angles.count()) {
3553 index = 0;
3554 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003555 if (firstTime) {
3556 firstTime = false;
3557 }
caryclark@google.com47580692012-07-23 12:14:49 +00003558 } while (index != first);
3559 }
3560#endif
3561
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003562#if DEBUG_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003563 static char as_digit(int value) {
3564 return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
3565 }
3566
3567 int debugShowWindingValues(int slotCount, int ofInterest) const {
3568 if (!(1 << fID & ofInterest)) {
3569 return 0;
3570 }
3571 int sum = 0;
3572 SkTDArray<char> slots;
3573 slots.setCount(slotCount * 2);
3574 memset(slots.begin(), ' ', slotCount * 2);
3575 for (int i = 0; i < fTs.count(); ++i) {
3576 // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
3577 // continue;
3578 // }
3579 sum += fTs[i].fWindValue;
3580 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
3581 sum += fTs[i].fOppValue;
3582 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
3583 }
3584 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
3585 slots.begin() + slotCount);
3586 return sum;
3587 }
3588
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003589 bool debugVerifyWinding(int start, int end, int winding) const {
3590 const Span& span = fTs[SkMin32(start, end)];
3591 int spanWinding = span.fWindSum;
3592 if (spanWinding == SK_MinS32) {
3593 return true;
3594 }
3595 int spanSign = SkSign32(start - end);
3596 int signedVal = spanSign * span.fWindValue;
3597 if (signedVal < 0) {
3598 spanWinding -= signedVal;
3599 }
3600 return span.fWindSum == winding;
3601 }
3602#endif
3603
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003604private:
3605 const SkPoint* fPts;
3606 SkPath::Verb fVerb;
3607 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003608 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00003609 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00003610 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003611#if DEBUG_DUMP
3612 int fID;
3613#endif
3614};
3615
caryclark@google.comb9738012012-07-03 19:53:30 +00003616class Contour;
3617
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003618struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003619 Contour* fContours[2];
3620 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003621 double fTs[2][2];
3622};
3623
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003624class Contour {
3625public:
3626 Contour() {
3627 reset();
3628#if DEBUG_DUMP
3629 fID = ++gContourID;
3630#endif
3631 }
3632
3633 bool operator<(const Contour& rh) const {
3634 return fBounds.fTop == rh.fBounds.fTop
3635 ? fBounds.fLeft < rh.fBounds.fLeft
3636 : fBounds.fTop < rh.fBounds.fTop;
3637 }
3638
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003639 void addCoincident(int index, Contour* other, int otherIndex,
3640 const Intersections& ts, bool swap) {
3641 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003642 coincidence.fContours[0] = this; // FIXME: no need to store
caryclark@google.comb9738012012-07-03 19:53:30 +00003643 coincidence.fContours[1] = other;
3644 coincidence.fSegments[0] = index;
3645 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003646 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3647 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3648 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3649 coincidence.fTs[swap][0] = ts.fT[0][0];
3650 coincidence.fTs[swap][1] = ts.fT[0][1];
3651 coincidence.fTs[!swap][0] = ts.fT[1][0];
3652 coincidence.fTs[!swap][1] = ts.fT[1][1];
3653 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3654 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3655 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3656 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3657 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3658 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3659 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003660 }
3661
3662 void addCross(const Contour* crosser) {
3663#ifdef DEBUG_CROSS
3664 for (int index = 0; index < fCrosses.count(); ++index) {
3665 SkASSERT(fCrosses[index] != crosser);
3666 }
3667#endif
3668 *fCrosses.append() = crosser;
3669 }
3670
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003671 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003672 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003673 fContainsCurves = true;
3674 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003675
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003676 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003677 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003678 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003679 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003680
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003681 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3682 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3683 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003684
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003685 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003686 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003687 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003688 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003689 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003690
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003691 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3692 containsIntercepts();
3693 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3694 }
3695
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003696 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003697 return fBounds;
3698 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003699
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003700 void complete() {
3701 setBounds();
3702 fContainsIntercepts = false;
3703 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003704
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003705 void containsIntercepts() {
3706 fContainsIntercepts = true;
3707 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003708
rmistry@google.comd6176b02012-08-23 18:14:13 +00003709 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003710 int &tIndex, double& hitT) {
3711 int segmentCount = fSegments.count();
3712 const Segment* bestSegment = NULL;
3713 for (int test = 0; test < segmentCount; ++test) {
3714 Segment* testSegment = &fSegments[test];
3715 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003716 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003717 continue;
3718 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003719 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003720 continue;
3721 }
3722 if (bounds.fLeft > basePt.fX) {
3723 continue;
3724 }
3725 if (bounds.fRight < basePt.fX) {
3726 continue;
3727 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003728 if (bounds.fLeft == bounds.fRight) {
3729 continue;
3730 }
3731 #if 0
3732 bool leftHalf = bounds.fLeft == basePt.fX;
3733 bool rightHalf = bounds.fRight == basePt.fX;
3734 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3735 basePt, leftHalf, rightHalf)) {
3736 continue;
3737 }
3738 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003739 double testHitT;
3740 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3741 if (testT >= 0) {
3742 bestSegment = testSegment;
3743 tIndex = testT;
3744 hitT = testHitT;
3745 }
3746 }
3747 return bestSegment;
3748 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003749
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003750 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003751 for (int index = 0; index < fCrosses.count(); ++index) {
3752 if (fCrosses[index] == crosser) {
3753 return true;
3754 }
3755 }
3756 return false;
3757 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003758
caryclark@google.comf839c032012-10-26 21:03:50 +00003759 const SkPoint& end() const {
3760 const Segment& segment = fSegments.back();
3761 return segment.pts()[segment.verb()];
3762 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003763
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003764 void findTooCloseToCall(bool otherXor) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003765 int segmentCount = fSegments.count();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003766 otherXor ^= fXor;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003767 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003768 fSegments[sIndex].findTooCloseToCall(fXor, otherXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003769 }
3770 }
3771
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003772 void fixOtherTIndex() {
3773 int segmentCount = fSegments.count();
3774 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3775 fSegments[sIndex].fixOtherTIndex();
3776 }
3777 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003778
caryclark@google.com31143cf2012-11-09 22:14:19 +00003779 bool operand() const {
3780 return fOperand;
3781 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003782
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003783 void reset() {
3784 fSegments.reset();
3785 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003786 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003787 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003788
caryclark@google.com235f56a2012-09-14 14:19:30 +00003789 // FIXME: for binary ops, need to keep both ops winding contributions separately
3790 // in edge array
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003791 void resolveCoincidence(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003792 int count = fCoincidences.count();
3793 for (int index = 0; index < count; ++index) {
3794 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003795 Contour* thisContour = coincidence.fContours[0];
3796 Contour* otherContour = coincidence.fContours[1];
3797 int thisIndex = coincidence.fSegments[0];
3798 int otherIndex = coincidence.fSegments[1];
3799 Segment& thisOne = thisContour->fSegments[thisIndex];
3800 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003801 #if DEBUG_CONCIDENT
3802 thisOne.debugShowTs();
3803 other.debugShowTs();
3804 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003805 double startT = coincidence.fTs[0][0];
3806 double endT = coincidence.fTs[0][1];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003807 bool cancelers = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003808 if (startT > endT) {
3809 SkTSwap<double>(startT, endT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003810 cancelers ^= true; // FIXME: just assign true
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003811 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003812 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003813 double oStartT = coincidence.fTs[1][0];
3814 double oEndT = coincidence.fTs[1][1];
3815 if (oStartT > oEndT) {
3816 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003817 cancelers ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003818 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003819 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003820 bool opp = thisContour->fOperand ^ otherContour->fOperand;
3821 if (cancelers && !opp) {
3822 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00003823 if (startT > 0 || oEndT < 1
3824 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3825 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003826 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003827 if (oStartT > 0 || endT < 1
3828 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3829 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003830 }
3831 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003832 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003833 if (startT > 0 || oStartT > 0
3834 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003835 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003836 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003837 if (endT < 1 || oEndT < 1
3838 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003839 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003840 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003841 thisOne.addTCoincident(thisContour->fXor, otherContour->fXor,
3842 startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003843 }
caryclark@google.com47580692012-07-23 12:14:49 +00003844 #if DEBUG_CONCIDENT
3845 thisOne.debugShowTs();
3846 other.debugShowTs();
3847 #endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003848 #if DEBUG_WINDING
3849 debugShowWindingValues(contourList);
3850 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003851 }
3852 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003853
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003854 const SkTArray<Segment>& segments() {
3855 return fSegments;
3856 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003857
caryclark@google.com235f56a2012-09-14 14:19:30 +00003858 void setOperand(bool isOp) {
3859 fOperand = isOp;
3860 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003861
caryclark@google.com235f56a2012-09-14 14:19:30 +00003862 void setXor(bool isXor) {
3863 fXor = isXor;
3864 }
3865
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003866 void sortSegments() {
3867 int segmentCount = fSegments.count();
3868 fSortedSegments.setReserve(segmentCount);
3869 for (int test = 0; test < segmentCount; ++test) {
3870 *fSortedSegments.append() = &fSegments[test];
3871 }
3872 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
3873 fFirstSorted = 0;
3874 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003875
caryclark@google.comf839c032012-10-26 21:03:50 +00003876 const SkPoint& start() const {
3877 return fSegments.front().pts()[0];
3878 }
3879
3880 void toPath(PathWrapper& path) const {
3881 int segmentCount = fSegments.count();
3882 const SkPoint& pt = fSegments.front().pts()[0];
3883 path.deferredMove(pt);
3884 for (int test = 0; test < segmentCount; ++test) {
3885 fSegments[test].addCurveTo(0, 1, path, true);
3886 }
3887 path.close();
3888 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003889
caryclark@google.comf839c032012-10-26 21:03:50 +00003890 void toPartialBackward(PathWrapper& path) const {
3891 int segmentCount = fSegments.count();
3892 for (int test = segmentCount - 1; test >= 0; --test) {
3893 fSegments[test].addCurveTo(1, 0, path, true);
3894 }
3895 }
3896
3897 void toPartialForward(PathWrapper& path) const {
3898 int segmentCount = fSegments.count();
3899 for (int test = 0; test < segmentCount; ++test) {
3900 fSegments[test].addCurveTo(0, 1, path, true);
3901 }
3902 }
3903
3904#if 0 // FIXME: obsolete, remove
caryclark@google.com15fa1382012-05-07 20:49:36 +00003905 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3906 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003907 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003908 // segments' top, and not a true sort, so it could be ameniable to regular
3909 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003910 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003911 int segmentCount = fSegments.count();
3912 SkASSERT(segmentCount > 0);
3913 int best = -1;
3914 Segment* bestSegment = NULL;
3915 while (++best < segmentCount) {
3916 Segment* testSegment = &fSegments[best];
3917 if (testSegment->done()) {
3918 continue;
3919 }
3920 bestSegment = testSegment;
3921 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003922 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003923 if (!bestSegment) {
3924 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003925 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003926 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003927 for (int test = best + 1; test < segmentCount; ++test) {
3928 Segment* testSegment = &fSegments[test];
3929 if (testSegment->done()) {
3930 continue;
3931 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003932 if (testSegment->bounds().fTop > bestTop) {
3933 continue;
3934 }
3935 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003936 if (bestTop > testTop) {
3937 bestTop = testTop;
3938 bestSegment = testSegment;
3939 }
3940 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003941 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003942 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003943 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003944#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003945
caryclark@google.comf839c032012-10-26 21:03:50 +00003946 Segment* topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003947 int segmentCount = fSortedSegments.count();
3948 SkASSERT(segmentCount > 0);
3949 Segment* bestSegment = NULL;
caryclark@google.comf839c032012-10-26 21:03:50 +00003950 int sortedIndex = fFirstSorted;
3951 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
3952 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003953 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003954 if (sortedIndex == fFirstSorted) {
3955 ++fFirstSorted;
3956 }
3957 continue;
3958 }
3959 SkPoint testXY;
3960 testSegment->activeLeftTop(testXY);
3961 if (testXY.fY < topLeft.fY) {
3962 continue;
3963 }
3964 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
3965 continue;
3966 }
3967 if (bestXY.fY < testXY.fY) {
3968 continue;
3969 }
3970 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003971 continue;
3972 }
3973 bestSegment = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00003974 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003975 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003976 return bestSegment;
3977 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003978
caryclark@google.com24bec792012-08-20 12:43:57 +00003979 Segment* undoneSegment(int& start, int& end) {
3980 int segmentCount = fSegments.count();
3981 for (int test = 0; test < segmentCount; ++test) {
3982 Segment* testSegment = &fSegments[test];
3983 if (testSegment->done()) {
3984 continue;
3985 }
3986 testSegment->undoneSpan(start, end);
3987 return testSegment;
3988 }
3989 return NULL;
3990 }
3991
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003992 int updateSegment(int index, const SkPoint* pts) {
3993 Segment& segment = fSegments[index];
3994 segment.updatePts(pts);
3995 return segment.verb() + 1;
3996 }
3997
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003998#if DEBUG_TEST
3999 SkTArray<Segment>& debugSegments() {
4000 return fSegments;
4001 }
4002#endif
4003
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004004#if DEBUG_DUMP
4005 void dump() {
4006 int i;
4007 const char className[] = "Contour";
4008 const int tab = 4;
4009 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
4010 for (i = 0; i < fSegments.count(); ++i) {
4011 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
4012 className, i);
4013 fSegments[i].dump();
4014 }
4015 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
4016 tab + sizeof(className), className,
4017 fBounds.fLeft, fBounds.fTop,
4018 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004019 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
4020 className, fContainsIntercepts);
4021 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
4022 className, fContainsCurves);
4023 }
4024#endif
4025
caryclark@google.com027de222012-07-12 12:52:50 +00004026#if DEBUG_ACTIVE_SPANS
4027 void debugShowActiveSpans() {
4028 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004029 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00004030 }
4031 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004032
4033 void validateActiveSpans() {
4034 for (int index = 0; index < fSegments.count(); ++index) {
4035 fSegments[index].validateActiveSpans();
4036 }
4037 }
caryclark@google.com027de222012-07-12 12:52:50 +00004038#endif
4039
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004040#if DEBUG_WINDING
4041 int debugShowWindingValues(int totalSegments, int ofInterest) {
4042 int count = fSegments.count();
4043 int sum = 0;
4044 for (int index = 0; index < count; ++index) {
4045 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
4046 }
4047 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
4048 return sum;
4049 }
4050
4051 static void debugShowWindingValues(SkTDArray<Contour*>& contourList) {
4052 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
4053 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
4054 int ofInterest = 1 << 5 | 1 << 8;
4055 int total = 0;
4056 int index;
4057 for (index = 0; index < contourList.count(); ++index) {
4058 total += contourList[index]->segments().count();
4059 }
4060 int sum = 0;
4061 for (index = 0; index < contourList.count(); ++index) {
4062 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
4063 }
4064 // SkDebugf("%s total=%d\n", __FUNCTION__, sum);
4065 }
4066#endif
4067
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004068protected:
4069 void setBounds() {
4070 int count = fSegments.count();
4071 if (count == 0) {
4072 SkDebugf("%s empty contour\n", __FUNCTION__);
4073 SkASSERT(0);
4074 // FIXME: delete empty contour?
4075 return;
4076 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004077 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004078 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004079 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004080 }
4081 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004082
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004083private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004084 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004085 SkTDArray<Segment*> fSortedSegments;
4086 int fFirstSorted;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004087 SkTDArray<Coincidence> fCoincidences;
4088 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004089 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004090 bool fContainsIntercepts;
4091 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004092 bool fOperand; // true for the second argument to a binary operator
4093 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004094#if DEBUG_DUMP
4095 int fID;
4096#endif
4097};
4098
4099class EdgeBuilder {
4100public:
4101
caryclark@google.comf839c032012-10-26 21:03:50 +00004102EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
4103 : fPath(path.nativePath())
4104 , fContours(contours)
4105{
4106 init();
4107}
4108
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004109EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00004110 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004111 , fContours(contours)
4112{
caryclark@google.comf839c032012-10-26 21:03:50 +00004113 init();
4114}
4115
4116void init() {
4117 fCurrentContour = NULL;
4118 fOperand = false;
4119 fXorMask = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004120#if DEBUG_DUMP
4121 gContourID = 0;
4122 gSegmentID = 0;
4123#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00004124 fSecondHalf = preFetch();
4125}
4126
4127void addOperand(const SkPath& path) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004128 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
4129 fPathVerbs.pop();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004130 fPath = &path;
caryclark@google.comf839c032012-10-26 21:03:50 +00004131 fXorMask = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004132 preFetch();
4133}
4134
4135void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004136 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004137 complete();
4138 if (fCurrentContour && !fCurrentContour->segments().count()) {
4139 fContours.pop_back();
4140 }
4141 // correct pointers in contours since fReducePts may have moved as it grew
4142 int cIndex = 0;
4143 int extraCount = fExtra.count();
4144 SkASSERT(extraCount == 0 || fExtra[0] == -1);
4145 int eIndex = 0;
4146 int rIndex = 0;
4147 while (++eIndex < extraCount) {
4148 int offset = fExtra[eIndex];
4149 if (offset < 0) {
4150 ++cIndex;
4151 continue;
4152 }
4153 fCurrentContour = &fContours[cIndex];
4154 rIndex += fCurrentContour->updateSegment(offset - 1,
4155 &fReducePts[rIndex]);
4156 }
4157 fExtra.reset(); // we're done with this
4158}
4159
4160ShapeOpMask xorMask() const {
4161 return fXorMask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004162}
4163
4164protected:
4165
4166void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004167 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004168 fCurrentContour->complete();
4169 fCurrentContour = NULL;
4170 }
4171}
4172
caryclark@google.com235f56a2012-09-14 14:19:30 +00004173// FIXME:remove once we can access path pts directly
4174int preFetch() {
4175 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004176 SkPoint pts[4];
4177 SkPath::Verb verb;
4178 do {
4179 verb = iter.next(pts);
4180 *fPathVerbs.append() = verb;
4181 if (verb == SkPath::kMove_Verb) {
4182 *fPathPts.append() = pts[0];
4183 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
4184 fPathPts.append(verb, &pts[1]);
4185 }
4186 } while (verb != SkPath::kDone_Verb);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004187 return fPathVerbs.count() - 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004188}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004189
caryclark@google.com235f56a2012-09-14 14:19:30 +00004190void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004191 SkPath::Verb reducedVerb;
4192 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004193 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004194 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004195 const SkPoint* finalCurveStart = NULL;
4196 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004197 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004198 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
4199 switch (verb) {
4200 case SkPath::kMove_Verb:
4201 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004202 if (!fCurrentContour) {
4203 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004204 fCurrentContour->setOperand(fOperand);
4205 fCurrentContour->setXor(fXorMask == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004206 *fExtra.append() = -1; // start new contour
4207 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004208 finalCurveEnd = pointsPtr++;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004209 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004210 case SkPath::kLine_Verb:
4211 // skip degenerate points
4212 if (pointsPtr[-1].fX != pointsPtr[0].fX
4213 || pointsPtr[-1].fY != pointsPtr[0].fY) {
4214 fCurrentContour->addLine(&pointsPtr[-1]);
4215 }
4216 break;
4217 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00004218
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004219 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
4220 if (reducedVerb == 0) {
4221 break; // skip degenerate points
4222 }
4223 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00004224 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004225 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004226 break;
4227 }
4228 fCurrentContour->addQuad(&pointsPtr[-1]);
4229 break;
4230 case SkPath::kCubic_Verb:
4231 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
4232 if (reducedVerb == 0) {
4233 break; // skip degenerate points
4234 }
4235 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004236 *fExtra.append() =
4237 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004238 break;
4239 }
4240 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004241 *fExtra.append() =
4242 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004243 break;
4244 }
4245 fCurrentContour->addCubic(&pointsPtr[-1]);
4246 break;
4247 case SkPath::kClose_Verb:
4248 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004249 if (finalCurveStart && finalCurveEnd
4250 && *finalCurveStart != *finalCurveEnd) {
4251 *fReducePts.append() = *finalCurveStart;
4252 *fReducePts.append() = *finalCurveEnd;
4253 *fExtra.append() =
4254 fCurrentContour->addLine(fReducePts.end() - 2);
4255 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004256 complete();
caryclark@google.com31143cf2012-11-09 22:14:19 +00004257 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004258 default:
4259 SkDEBUGFAIL("bad verb");
4260 return;
4261 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004262 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004263 pointsPtr += verb;
4264 SkASSERT(fCurrentContour);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004265 nextVerb:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004266 if (verbPtr == endOfFirstHalf) {
4267 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004268 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004269 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004270}
4271
4272private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004273 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004274 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004275 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004276 Contour* fCurrentContour;
4277 SkTArray<Contour>& fContours;
4278 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004279 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com235f56a2012-09-14 14:19:30 +00004280 ShapeOpMask fXorMask;
4281 int fSecondHalf;
4282 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004283};
4284
4285class Work {
4286public:
4287 enum SegmentType {
4288 kHorizontalLine_Segment = -1,
4289 kVerticalLine_Segment = 0,
4290 kLine_Segment = SkPath::kLine_Verb,
4291 kQuad_Segment = SkPath::kQuad_Verb,
4292 kCubic_Segment = SkPath::kCubic_Verb,
4293 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004294
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004295 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4296 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4297 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004298
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004299 // FIXME: does it make sense to write otherIndex now if we're going to
4300 // fix it up later?
4301 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004302 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004303 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004304
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004305 // Avoid collapsing t values that are close to the same since
4306 // we walk ts to describe consecutive intersections. Since a pair of ts can
4307 // be nearly equal, any problems caused by this should be taken care
4308 // of later.
4309 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004310 int addT(double newT, const Work& other) {
4311 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004312 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004313
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004314 bool advance() {
4315 return ++fIndex < fLast;
4316 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004317
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004318 SkScalar bottom() const {
4319 return bounds().fBottom;
4320 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004321
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004322 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004323 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004324 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004325
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004326 const SkPoint* cubic() const {
4327 return fCubic;
4328 }
4329
4330 void init(Contour* contour) {
4331 fContour = contour;
4332 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004333 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004334 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004335
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004336 bool isAdjacent(const Work& next) {
4337 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4338 }
4339
4340 bool isFirstLast(const Work& next) {
4341 return fContour == next.fContour && fIndex == 0
4342 && next.fIndex == fLast - 1;
4343 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004344
4345 SkScalar left() const {
4346 return bounds().fLeft;
4347 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004348
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004349 void promoteToCubic() {
4350 fCubic[0] = pts()[0];
4351 fCubic[2] = pts()[1];
4352 fCubic[3] = pts()[2];
4353 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4354 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4355 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4356 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4357 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004358
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004359 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004360 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004361 }
4362
4363 SkScalar right() const {
4364 return bounds().fRight;
4365 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004366
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004367 ptrdiff_t segmentIndex() const {
4368 return fIndex;
4369 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004370
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004371 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004372 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004373 SegmentType type = (SegmentType) segment.verb();
4374 if (type != kLine_Segment) {
4375 return type;
4376 }
4377 if (segment.isHorizontal()) {
4378 return kHorizontalLine_Segment;
4379 }
4380 if (segment.isVertical()) {
4381 return kVerticalLine_Segment;
4382 }
4383 return kLine_Segment;
4384 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004385
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004386 bool startAfter(const Work& after) {
4387 fIndex = after.fIndex;
4388 return advance();
4389 }
4390
4391 SkScalar top() const {
4392 return bounds().fTop;
4393 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004394
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004395 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004396 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004397 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004398
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004399 SkScalar x() const {
4400 return bounds().fLeft;
4401 }
4402
4403 bool xFlipped() const {
4404 return x() != pts()[0].fX;
4405 }
4406
4407 SkScalar y() const {
4408 return bounds().fTop;
4409 }
4410
4411 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004412 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004413 }
4414
4415protected:
4416 Contour* fContour;
4417 SkPoint fCubic[4];
4418 int fIndex;
4419 int fLast;
4420};
4421
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004422#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004423static void debugShowLineIntersection(int pts, const Work& wt,
4424 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004425 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004426 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004427 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4428 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4429 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4430 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004431 return;
4432 }
4433 SkPoint wtOutPt, wnOutPt;
4434 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4435 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004436 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 +00004437 __FUNCTION__,
4438 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4439 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4440 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004441 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004442 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004443 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004444 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4445 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4446 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004447 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4448 }
4449 SkDebugf("\n");
4450}
4451
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004452static void debugShowQuadLineIntersection(int pts, const Work& wt,
4453 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4454 if (!pts) {
4455 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00004456 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004457 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4458 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004459 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004460 return;
4461 }
4462 SkPoint wtOutPt, wnOutPt;
4463 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4464 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4465 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4466 __FUNCTION__,
4467 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4468 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4469 wtOutPt.fX, wtOutPt.fY);
4470 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004471 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
4472 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004473 }
4474 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4475 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4476 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4477 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004478 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
4479 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004480 }
4481 SkDebugf("\n");
4482}
4483
caryclark@google.coma461ff02012-10-11 12:54:23 +00004484static void debugShowQuadIntersection(int pts, const Work& wt,
4485 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4486 if (!pts) {
4487 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
4488 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
4489 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00004490 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00004491 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004492 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00004493 return;
4494 }
4495 SkPoint wtOutPt, wnOutPt;
4496 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4497 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4498 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4499 __FUNCTION__,
4500 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4501 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4502 wtOutPt.fX, wtOutPt.fY);
4503 if (pts == 2) {
4504 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
4505 }
4506 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4507 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4508 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
4509 wnOutPt.fX, wnOutPt.fY);
4510 if (pts == 2) {
4511 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004512 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004513 SkDebugf("\n");
4514}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004515#else
4516static void debugShowLineIntersection(int , const Work& ,
4517 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004518}
caryclark@google.coma461ff02012-10-11 12:54:23 +00004519
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004520static void debugShowQuadLineIntersection(int , const Work& ,
4521 const Work& , const double [2], const double [2]) {
4522}
4523
caryclark@google.coma461ff02012-10-11 12:54:23 +00004524static void debugShowQuadIntersection(int , const Work& ,
4525 const Work& , const double [2], const double [2]) {
4526}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004527#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004528
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004529static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004530
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004531 if (test != next) {
4532 if (test->bounds().fBottom < next->bounds().fTop) {
4533 return false;
4534 }
4535 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
4536 return true;
4537 }
4538 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004539 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004540 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004541 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004542 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004543 Work wn;
4544 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004545 if (test == next && !wn.startAfter(wt)) {
4546 continue;
4547 }
4548 do {
4549 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
4550 continue;
4551 }
4552 int pts;
4553 Intersections ts;
4554 bool swap = false;
4555 switch (wt.segmentType()) {
4556 case Work::kHorizontalLine_Segment:
4557 swap = true;
4558 switch (wn.segmentType()) {
4559 case Work::kHorizontalLine_Segment:
4560 case Work::kVerticalLine_Segment:
4561 case Work::kLine_Segment: {
4562 pts = HLineIntersect(wn.pts(), wt.left(),
4563 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004564 debugShowLineIntersection(pts, wt, wn,
4565 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004566 break;
4567 }
4568 case Work::kQuad_Segment: {
4569 pts = HQuadIntersect(wn.pts(), wt.left(),
4570 wt.right(), wt.y(), wt.xFlipped(), ts);
4571 break;
4572 }
4573 case Work::kCubic_Segment: {
4574 pts = HCubicIntersect(wn.pts(), wt.left(),
4575 wt.right(), wt.y(), wt.xFlipped(), ts);
4576 break;
4577 }
4578 default:
4579 SkASSERT(0);
4580 }
4581 break;
4582 case Work::kVerticalLine_Segment:
4583 swap = true;
4584 switch (wn.segmentType()) {
4585 case Work::kHorizontalLine_Segment:
4586 case Work::kVerticalLine_Segment:
4587 case Work::kLine_Segment: {
4588 pts = VLineIntersect(wn.pts(), wt.top(),
4589 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004590 debugShowLineIntersection(pts, wt, wn,
4591 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004592 break;
4593 }
4594 case Work::kQuad_Segment: {
4595 pts = VQuadIntersect(wn.pts(), wt.top(),
4596 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4597 break;
4598 }
4599 case Work::kCubic_Segment: {
4600 pts = VCubicIntersect(wn.pts(), wt.top(),
4601 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4602 break;
4603 }
4604 default:
4605 SkASSERT(0);
4606 }
4607 break;
4608 case Work::kLine_Segment:
4609 switch (wn.segmentType()) {
4610 case Work::kHorizontalLine_Segment:
4611 pts = HLineIntersect(wt.pts(), wn.left(),
4612 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004613 debugShowLineIntersection(pts, wt, wn,
4614 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004615 break;
4616 case Work::kVerticalLine_Segment:
4617 pts = VLineIntersect(wt.pts(), wn.top(),
4618 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004619 debugShowLineIntersection(pts, wt, wn,
4620 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004621 break;
4622 case Work::kLine_Segment: {
4623 pts = LineIntersect(wt.pts(), wn.pts(), ts);
4624 debugShowLineIntersection(pts, wt, wn,
4625 ts.fT[1], ts.fT[0]);
4626 break;
4627 }
4628 case Work::kQuad_Segment: {
4629 swap = true;
4630 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004631 debugShowQuadLineIntersection(pts, wn, wt,
4632 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004633 break;
4634 }
4635 case Work::kCubic_Segment: {
4636 swap = true;
4637 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
4638 break;
4639 }
4640 default:
4641 SkASSERT(0);
4642 }
4643 break;
4644 case Work::kQuad_Segment:
4645 switch (wn.segmentType()) {
4646 case Work::kHorizontalLine_Segment:
4647 pts = HQuadIntersect(wt.pts(), wn.left(),
4648 wn.right(), wn.y(), wn.xFlipped(), ts);
4649 break;
4650 case Work::kVerticalLine_Segment:
4651 pts = VQuadIntersect(wt.pts(), wn.top(),
4652 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4653 break;
4654 case Work::kLine_Segment: {
4655 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004656 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004657 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004658 break;
4659 }
4660 case Work::kQuad_Segment: {
4661 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004662 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004663 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004664 break;
4665 }
4666 case Work::kCubic_Segment: {
4667 wt.promoteToCubic();
4668 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
4669 break;
4670 }
4671 default:
4672 SkASSERT(0);
4673 }
4674 break;
4675 case Work::kCubic_Segment:
4676 switch (wn.segmentType()) {
4677 case Work::kHorizontalLine_Segment:
4678 pts = HCubicIntersect(wt.pts(), wn.left(),
4679 wn.right(), wn.y(), wn.xFlipped(), ts);
4680 break;
4681 case Work::kVerticalLine_Segment:
4682 pts = VCubicIntersect(wt.pts(), wn.top(),
4683 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4684 break;
4685 case Work::kLine_Segment: {
4686 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
4687 break;
4688 }
4689 case Work::kQuad_Segment: {
4690 wn.promoteToCubic();
4691 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
4692 break;
4693 }
4694 case Work::kCubic_Segment: {
4695 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4696 break;
4697 }
4698 default:
4699 SkASSERT(0);
4700 }
4701 break;
4702 default:
4703 SkASSERT(0);
4704 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004705 if (!foundCommonContour && pts > 0) {
4706 test->addCross(next);
4707 next->addCross(test);
4708 foundCommonContour = true;
4709 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004710 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004711 if (pts == 2) {
4712 if (wn.segmentType() <= Work::kLine_Segment
4713 && wt.segmentType() <= Work::kLine_Segment) {
4714 wt.addCoincident(wn, ts, swap);
4715 continue;
4716 }
4717 if (wn.segmentType() == Work::kQuad_Segment
4718 && wt.segmentType() == Work::kQuad_Segment
4719 && ts.coincidentUsed() == 2) {
4720 wt.addCoincident(wn, ts, swap);
4721 continue;
4722 }
4723
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004724 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004725 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004726 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4727 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004728 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4729 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004730 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4731 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004732 }
4733 } while (wn.advance());
4734 } while (wt.advance());
4735 return true;
4736}
4737
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004738// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004739// see if coincidence is formed by clipping non-concident segments
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004740static void coincidenceCheck(SkTDArray<Contour*>& contourList, bool otherXor, int total) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004741 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004742 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004743 Contour* contour = contourList[cIndex];
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004744 contour->resolveCoincidence(contourList);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004745 }
4746 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4747 Contour* contour = contourList[cIndex];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004748 contour->findTooCloseToCall(otherXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004749 }
4750}
4751
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004752// project a ray from the top of the contour up and see if it hits anything
4753// note: when we compute line intersections, we keep track of whether
4754// two contours touch, so we need only look at contours not touching this one.
4755// OPTIMIZATION: sort contourList vertically to avoid linear walk
4756static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com31143cf2012-11-09 22:14:19 +00004757 const Segment* current, int index, int endIndex, bool opp) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004758 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004759 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004760 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00004761 const Segment* test = NULL;
4762 int tIndex;
4763 double tHit;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004764 for (int cTest = 0; cTest < contourCount; ++cTest) {
4765 Contour* contour = contourList[cTest];
caryclark@google.com31143cf2012-11-09 22:14:19 +00004766 if (contour->operand() ^ current->operand() != opp) {
4767 continue;
4768 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004769 if (basePt.fY < contour->bounds().fTop) {
4770 continue;
4771 }
4772 if (bestY > contour->bounds().fBottom) {
4773 continue;
4774 }
caryclark@google.com47580692012-07-23 12:14:49 +00004775 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
4776 if (next) {
4777 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004778 }
caryclark@google.com47580692012-07-23 12:14:49 +00004779 }
4780 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00004781 return 0;
4782 }
4783 int winding, windValue;
4784 // If the ray hit the end of a span, we need to construct the wheel of
4785 // angles to find the span closest to the ray -- even if there are just
4786 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004787 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004788 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00004789 SkTDArray<Angle> angles;
4790 int end = test->nextSpan(tIndex, 1);
4791 if (end < 0) {
4792 end = test->nextSpan(tIndex, -1);
4793 }
4794 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00004795 SkASSERT(angles.count() > 0);
4796 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
4797#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00004798 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00004799#endif
4800 return 0;
4801 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004802 test->buildAngles(tIndex, angles, false);
caryclark@google.com47580692012-07-23 12:14:49 +00004803 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00004804 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00004805 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004806 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00004807 // hour after 6 o'clock
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004808 (void) Segment::SortAngles(angles, sorted);
caryclark@google.com47580692012-07-23 12:14:49 +00004809#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004810 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00004811#endif
4812 // walk the sorted angle fan to find the lowest angle
4813 // above the base point. Currently, the first angle in the sorted array
4814 // is 12 noon or an earlier hour (the next counterclockwise)
4815 int count = sorted.count();
4816 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004817 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00004818 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004819 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00004820 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00004821 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004822 if (angle->unsortable()) {
4823 continue;
4824 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004825 if (baseMatches && angle->isHorizontal()) {
4826 continue;
4827 }
4828 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00004829 test = angle->segment();
4830 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
4831 const SkPoint* pts = test->pts();
4832 indexDx = pts[2].fX - pts[1].fX - indexDx;
4833 }
caryclark@google.com47580692012-07-23 12:14:49 +00004834 if (indexDx < 0) {
4835 left = index;
4836 } else if (indexDx > 0) {
4837 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00004838 int previous = index - 1;
4839 if (previous < 0) {
4840 previous = count - 1;
4841 }
4842 const Angle* prev = sorted[previous];
4843 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
4844#if DEBUG_SORT
4845 SkDebugf("%s use prev\n", __FUNCTION__);
4846#endif
4847 right = previous;
4848 }
caryclark@google.com47580692012-07-23 12:14:49 +00004849 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004850 } else {
4851 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004852 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004853 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004854 if (left < 0 && right < 0) {
4855 left = mid;
4856 }
caryclark@google.com47580692012-07-23 12:14:49 +00004857 SkASSERT(left >= 0 || right >= 0);
4858 if (left < 0) {
4859 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004860 } else if (left >= 0 && mid >= 0 && right >= 0
4861 && sorted[mid]->sign() == sorted[right]->sign()) {
4862 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004863 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004864 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004865 test = angle->segment();
4866 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004867 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004868 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004869#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004870 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4871 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004872#endif
4873 } else {
4874 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004875 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004876 windValue = test->windValue(tIndex);
4877#if DEBUG_WINDING
4878 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4879 windValue);
4880#endif
4881 }
4882 // see if a + change in T results in a +/- change in X (compute x'(T))
4883 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004884 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4885 const SkPoint* pts = test->pts();
4886 dx = pts[2].fX - pts[1].fX - dx;
4887 }
caryclark@google.com47580692012-07-23 12:14:49 +00004888#if DEBUG_WINDING
4889 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4890#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004891 SkASSERT(dx != 0);
4892 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004893 winding += dx > 0 ? -windValue : windValue;
4894#if DEBUG_WINDING
4895 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4896#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004897 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004898 return winding;
4899}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004900
caryclark@google.com24bec792012-08-20 12:43:57 +00004901static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4902 int contourCount = contourList.count();
4903 Segment* result;
4904 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4905 Contour* contour = contourList[cIndex];
4906 result = contour->undoneSegment(start, end);
4907 if (result) {
4908 return result;
4909 }
4910 }
4911 return NULL;
4912}
4913
4914
4915
caryclark@google.com31143cf2012-11-09 22:14:19 +00004916static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004917 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004918 Span* span;
4919 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004920 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4921 Segment* segment = backPtr.fOther;
4922 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004923 SkTDArray<Angle> angles;
4924 int done = 0;
4925 if (segment->activeAngle(tIndex, done, angles)) {
4926 Angle* last = angles.end() - 1;
4927 tIndex = last->start();
4928 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00004929 #if TRY_ROTATE
4930 *chase.insert(0) = span;
4931 #else
4932 *chase.append() = span;
4933 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004934 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004935 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004936 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004937 continue;
4938 }
4939 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004940 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004941#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004942 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004943#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004944 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004945 continue;
4946 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004947 // find first angle, initialize winding to computed fWindSum
4948 int firstIndex = -1;
4949 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004950 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004951 do {
4952 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004953 segment = angle->segment();
4954 winding = segment->windSum(angle);
4955 } while (winding == SK_MinS32);
4956 int spanWinding = segment->spanSign(angle->start(), angle->end());
4957 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00004958 SkDebugf("%s winding=%d spanWinding=%d\n",
4959 __FUNCTION__, winding, spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00004960 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00004961 // turn span winding into contour winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004962 if (spanWinding * winding < 0) {
4963 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004964 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004965 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004966 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004967 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004968 // we care about first sign and whether wind sum indicates this
4969 // edge is inside or outside. Maybe need to pass span winding
4970 // or first winding or something into this function?
4971 // advance to first undone angle, then return it and winding
4972 // (to set whether edges are active or not)
4973 int nextIndex = firstIndex + 1;
4974 int angleCount = sorted.count();
4975 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004976 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00004977 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00004978 do {
4979 SkASSERT(nextIndex != firstIndex);
4980 if (nextIndex == angleCount) {
4981 nextIndex = 0;
4982 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004983 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00004984 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004985 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00004986 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004987 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00004988 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
4989 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004990 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004991 tIndex = angle->start();
4992 endIndex = angle->end();
4993 int lesser = SkMin32(tIndex, endIndex);
4994 const Span& nextSpan = segment->span(lesser);
4995 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004996#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00004997 // FIXME: this be wrong. assign startWinding if edge is in
4998 // same direction. If the direction is opposite, winding to
4999 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00005000 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005001 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005002 }
caryclark@google.com59823f72012-08-09 18:17:47 +00005003 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005004#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005005 break;
5006 }
5007 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005008 #if TRY_ROTATE
5009 *chase.insert(0) = span;
5010 #else
5011 *chase.append() = span;
5012 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005013 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005014 }
5015 return NULL;
5016}
5017
caryclark@google.com027de222012-07-12 12:52:50 +00005018#if DEBUG_ACTIVE_SPANS
5019static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005020 int index;
5021 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00005022 contourList[index]->debugShowActiveSpans();
5023 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005024 for (index = 0; index < contourList.count(); ++ index) {
5025 contourList[index]->validateActiveSpans();
5026 }
caryclark@google.com027de222012-07-12 12:52:50 +00005027}
5028#endif
5029
caryclark@google.com27c449a2012-07-27 18:26:38 +00005030static bool windingIsActive(int winding, int spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005031 // FIXME: !spanWinding test must be superflorous, true?
caryclark@google.com27c449a2012-07-27 18:26:38 +00005032 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
5033 && (!winding || !spanWinding || winding == -spanWinding);
5034}
5035
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005036static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comf839c032012-10-26 21:03:50 +00005037 int& endIndex, SkPoint& topLeft) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005038 Segment* result;
5039 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00005040 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005041 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00005042 Segment* topStart = NULL;
5043 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5044 Contour* contour = contourList[cIndex];
5045 const Bounds& bounds = contour->bounds();
5046 if (bounds.fBottom < topLeft.fY) {
5047 continue;
5048 }
5049 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
5050 continue;
5051 }
5052 Segment* test = contour->topSortableSegment(topLeft, bestXY);
5053 if (test) {
5054 topStart = test;
5055 }
5056 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005057 if (!topStart) {
5058 return NULL;
5059 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005060 topLeft = bestXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005061 result = topStart->findTop(index, endIndex);
5062 } while (!result);
5063 return result;
5064}
caryclark@google.com31143cf2012-11-09 22:14:19 +00005065
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005066static int updateWindings(const Segment* current, int index, int endIndex, int& spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005067 int lesser = SkMin32(index, endIndex);
5068 spanWinding = current->spanSign(index, endIndex);
5069 int winding = current->windSum(lesser);
5070 bool inner = useInnerWinding(winding - spanWinding, winding);
5071#if DEBUG_WINDING
5072 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
5073 " inner=%d result=%d\n",
5074 __FUNCTION__, current->debugID(), current->t(lesser),
5075 spanWinding, winding, SkSign32(index - endIndex),
5076 useInnerWinding(winding - spanWinding, winding),
5077 inner ? winding - spanWinding : winding);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005078#endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005079 if (inner) {
5080 winding -= spanWinding;
5081 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005082 return winding;
5083}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005084
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005085// Each segment may have an inside or an outside. Segments contained within
5086// winding may have insides on either side, and form a contour that should be
5087// ignored. Segments that are coincident with opposing direction segments may
5088// have outsides on either side, and should also disappear.
5089// 'Normal' segments will have one inside and one outside. Subsequent connections
5090// when winding should follow the intersection direction. If more than one edge
5091// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005092 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00005093 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005094// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005095static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005096 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005097 bool unsortable = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005098 bool closable = true;
5099 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com15fa1382012-05-07 20:49:36 +00005100 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005101 int index, endIndex;
caryclark@google.com31143cf2012-11-09 22:14:19 +00005102 // iterates while top is unsortable
caryclark@google.comf839c032012-10-26 21:03:50 +00005103 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005104 if (!current) {
5105 break;
5106 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005107 int contourWinding;
5108 if (firstContour) {
5109 contourWinding = 0;
5110 firstContour = false;
5111 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00005112 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005113 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00005114 if (sumWinding == SK_MinS32) {
caryclark@google.com6ec15262012-11-16 20:16:50 +00005115 sumWinding = current->computeSum(index, endIndex, NULL);
caryclark@google.com200c2112012-08-03 15:05:04 +00005116 }
5117 if (sumWinding == SK_MinS32) {
5118 contourWinding = innerContourCheck(contourList, current,
caryclark@google.com31143cf2012-11-09 22:14:19 +00005119 index, endIndex, false);
caryclark@google.com200c2112012-08-03 15:05:04 +00005120 } else {
5121 contourWinding = sumWinding;
5122 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00005123 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
5124 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00005125 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005126 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00005127#if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005128 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n",
5129 __FUNCTION__, sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00005130 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00005131#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005132 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005133#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005134 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005135 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
5136#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005137 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005138 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005139 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005140 // FIXME: needs work. While it works in limited situations, it does
5141 // not always compute winding correctly. Active should be removed and instead
5142 // the initial winding should be correctly passed in so that if the
5143 // inner contour is wound the same way, it never finds an accumulated
5144 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00005145 // other than zero when resolving sorted angles.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005146 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005147 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005148 bool active = windingIsActive(winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005149 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00005150 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
5151 __FUNCTION__, active ? "true" : "false",
5152 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005153 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005154 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005155 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005156 int nextStart = index;
5157 int nextEnd = endIndex;
5158 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005159 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005160 if (!next) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005161 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00005162 && current->verb() != SkPath::kLine_Verb
5163 && !simple.isClosed()) {
5164 current->addCurveTo(index, endIndex, simple, true);
5165 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005166 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005167 break;
5168 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005169 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005170 current = next;
5171 index = nextStart;
5172 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005173 } while (!simple.isClosed()
5174 && ((active && !unsortable) || !current->done()));
5175 if (active) {
5176 if (!simple.isClosed()) {
5177 SkASSERT(unsortable);
5178 int min = SkMin32(index, endIndex);
5179 if (!current->done(min)) {
5180 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.com31143cf2012-11-09 22:14:19 +00005181 current->markDone(SkMin32(index, endIndex),
5182 winding ? winding : spanWinding);
caryclark@google.comf839c032012-10-26 21:03:50 +00005183 }
5184 closable = false;
5185 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005186 simple.close();
5187 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005188 current = findChase(chaseArray, index, endIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005189 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00005190 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005191 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005192 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005193 break;
5194 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005195 winding = updateWindings(current, index, endIndex, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005196 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005197 } while (true);
caryclark@google.comf839c032012-10-26 21:03:50 +00005198 return closable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005199}
5200
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005201// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005202static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005203 Segment* current;
5204 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005205 bool unsortable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00005206 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005207 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005208 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005209 int nextStart = start;
5210 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005211 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00005212 if (!next) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005213 if (simple.hasMove()
5214 && current->verb() != SkPath::kLine_Verb
5215 && !simple.isClosed()) {
5216 current->addCurveTo(start, end, simple, true);
5217 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005218 }
caryclark@google.com24bec792012-08-20 12:43:57 +00005219 break;
5220 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005221 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00005222 current = next;
5223 start = nextStart;
5224 end = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005225 } while (!simple.isClosed());
5226 // FIXME: add unsortable test
5227 if (simple.hasMove()) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005228 simple.close();
5229 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005230 #if DEBUG_ACTIVE_SPANS
5231 debugShowActiveSpans(contourList);
5232 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005233 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005234 return !unsortable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005235}
5236
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005237static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5238 int contourCount = contourList.count();
5239 for (int cTest = 0; cTest < contourCount; ++cTest) {
5240 Contour* contour = contourList[cTest];
5241 contour->fixOtherTIndex();
5242 }
5243}
5244
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005245static void sortSegments(SkTDArray<Contour*>& contourList) {
5246 int contourCount = contourList.count();
5247 for (int cTest = 0; cTest < contourCount; ++cTest) {
5248 Contour* contour = contourList[cTest];
5249 contour->sortSegments();
5250 }
5251}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005252
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005253static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005254 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005255 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005256 if (count == 0) {
5257 return;
5258 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005259 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005260 *list.append() = &contours[index];
5261 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005262 QSort<Contour>(list.begin(), list.end() - 1);
5263}
5264
caryclark@google.comf839c032012-10-26 21:03:50 +00005265static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005266 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005267}
5268
caryclark@google.comf839c032012-10-26 21:03:50 +00005269 /*
5270 check start and end of each contour
5271 if not the same, record them
5272 match them up
5273 connect closest
5274 reassemble contour pieces into new path
5275 */
5276static void assemble(const PathWrapper& path, PathWrapper& simple) {
5277#if DEBUG_PATH_CONSTRUCTION
5278 SkDebugf("%s\n", __FUNCTION__);
5279#endif
5280 SkTArray<Contour> contours;
5281 EdgeBuilder builder(path, contours);
5282 builder.finish();
5283 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005284 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005285 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00005286 for (outer = 0; outer < count; ++outer) {
5287 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005288 const SkPoint& eStart = eContour.start();
5289 const SkPoint& eEnd = eContour.end();
5290 if (approximatelyEqual(eStart, eEnd)) {
5291 eContour.toPath(simple);
5292 continue;
5293 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005294 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005295 }
5296 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005297 if (count == 0) {
5298 return;
5299 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005300 SkTDArray<int> sLink, eLink;
5301 sLink.setCount(count);
5302 eLink.setCount(count);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005303 SkTDArray<double> sBest, eBest;
5304 sBest.setCount(count);
5305 eBest.setCount(count);
caryclark@google.comf839c032012-10-26 21:03:50 +00005306 int rIndex;
5307 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005308 outer = runs[rIndex];
5309 const Contour& oContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005310 const SkPoint& oStart = oContour.start();
5311 const SkPoint& oEnd = oContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005312 double dx = oEnd.fX - oStart.fX;
5313 double dy = oEnd.fY - oStart.fY;
5314 double dist = dx * dx + dy * dy;
5315 sBest[rIndex] = eBest[rIndex] = dist;
5316 sLink[rIndex] = eLink[rIndex] = rIndex;
5317 }
5318 for (rIndex = 0; rIndex < count - 1; ++rIndex) {
5319 outer = runs[rIndex];
5320 const Contour& oContour = contours[outer];
5321 const SkPoint& oStart = oContour.start();
5322 const SkPoint& oEnd = oContour.end();
5323 double bestStartDist = sBest[rIndex];
5324 double bestEndDist = eBest[rIndex];
5325 for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) {
5326 int inner = runs[iIndex];
5327 const Contour& iContour = contours[inner];
caryclark@google.comf839c032012-10-26 21:03:50 +00005328 const SkPoint& iStart = iContour.start();
5329 const SkPoint& iEnd = iContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005330 double dx = iStart.fX - oStart.fX;
5331 double dy = iStart.fY - oStart.fY;
5332 double dist = dx * dx + dy * dy;
5333 if (bestStartDist > dist) {
5334 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005335 sLink[rIndex] = ~iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005336 sLink[iIndex] = ~rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005337 }
5338 dx = iEnd.fX - oStart.fX;
5339 dy = iEnd.fY - oStart.fY;
5340 dist = dx * dx + dy * dy;
5341 if (bestStartDist > dist) {
5342 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005343 sLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005344 eLink[iIndex] = rIndex;
5345 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005346 dx = iStart.fX - oEnd.fX;
5347 dy = iStart.fY - oEnd.fY;
5348 dist = dx * dx + dy * dy;
5349 if (bestEndDist > dist) {
5350 bestEndDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005351 sLink[iIndex] = rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005352 eLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005353 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005354 dx = iEnd.fX - oEnd.fX;
5355 dy = iEnd.fY - oEnd.fY;
5356 dist = dx * dx + dy * dy;
5357 if (bestEndDist > dist) {
5358 bestEndDist = dist;
5359 eLink[iIndex] = ~rIndex;
5360 eLink[rIndex] = ~iIndex;
5361 }
5362 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005363 }
5364 rIndex = 0;
5365 bool forward = true;
5366 bool first = true;
5367 const SkPoint* startPtr;
5368 int sIndex = sLink[rIndex];
caryclark@google.com0b7da432012-10-31 19:00:20 +00005369 SkASSERT(sIndex != INT_MAX);
5370 sLink[rIndex] = INT_MAX;
5371 int eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005372 if (sIndex < 0) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005373 eIndex = sLink[~sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005374 sLink[~sIndex] = INT_MAX;
5375 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005376 eIndex = eLink[sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005377 eLink[sIndex] = INT_MAX;
5378 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005379 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005380 do {
5381 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005382 outer = runs[rIndex];
5383 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005384 if (first) {
5385 startPtr = &contour.start();
5386 first = false;
5387 simple.deferredMove(startPtr[0]);
5388 }
5389 const SkPoint* endPtr;
5390 if (forward) {
5391 contour.toPartialForward(simple);
5392 endPtr = &contour.end();
5393 } else {
5394 contour.toPartialBackward(simple);
5395 endPtr = &contour.start();
5396 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005397 if (sIndex == eIndex) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005398 simple.close();
5399 first = forward = true;
5400 break;
5401 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005402 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005403 eIndex = eLink[rIndex];
5404 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005405 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005406 if (eIndex >= 0) {
5407 SkASSERT(sLink[eIndex] == rIndex);
5408 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005409 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005410 SkASSERT(eLink[~eIndex] == ~rIndex);
5411 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005412 }
5413 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005414 eIndex = sLink[rIndex];
5415 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005416 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005417 if (eIndex >= 0) {
5418 SkASSERT(eLink[eIndex] == rIndex);
5419 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005420 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005421 SkASSERT(sLink[~eIndex] == ~rIndex);
5422 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005423 }
5424 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005425 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005426 if (rIndex < 0) {
5427 forward ^= 1;
5428 rIndex = ~rIndex;
5429 }
5430 } while (true);
5431 for (rIndex = 0; rIndex < count; ++rIndex) {
5432 if (sLink[rIndex] != INT_MAX) {
5433 break;
5434 }
5435 }
5436 } while (rIndex < count);
5437 SkASSERT(first);
5438}
5439
5440void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005441 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00005442 result.reset();
5443 result.setFillType(SkPath::kEvenOdd_FillType);
5444 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005445
5446 // turn path into list of segments
5447 SkTArray<Contour> contours;
5448 // FIXME: add self-intersecting cubics' T values to segment
5449 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00005450 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005451 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005452 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005453 Contour** currentPtr = contourList.begin();
5454 if (!currentPtr) {
5455 return;
5456 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005457 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005458 // find all intersections between segments
5459 do {
5460 Contour** nextPtr = currentPtr;
5461 Contour* current = *currentPtr++;
5462 Contour* next;
5463 do {
5464 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005465 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005466 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005467 // eat through coincident edges
caryclark@google.com7ba591e2012-11-20 14:21:54 +00005468 coincidenceCheck(contourList, false, 0);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00005469 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005470 sortSegments(contourList);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005471#if DEBUG_ACTIVE_SPANS
5472 debugShowActiveSpans(contourList);
5473#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005474 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005475 if (builder.xorMask() == kWinding_Mask
5476 ? !bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00005477 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005478 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00005479 SkPath temp;
5480 temp.setFillType(SkPath::kEvenOdd_FillType);
5481 PathWrapper assembled(temp);
5482 assemble(simple, assembled);
5483 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00005484 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005485}
5486