blob: 42610291e6752c01a99603558a94f5c01ef8f548 [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
caryclark@google.com47580692012-07-23 12:14:49 +000020// An Edge is a Segment generated from a Span
caryclark@google.com15fa1382012-05-07 20:49:36 +000021
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022// FIXME: remove once debugging is complete
caryclark@google.com47580692012-07-23 12:14:49 +000023#ifdef SK_DEBUG
24int gDebugMaxWindSum = SK_MaxS32;
25int gDebugMaxWindValue = SK_MaxS32;
26#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +000027
caryclark@google.comf839c032012-10-26 21:03:50 +000028#define PIN_ADD_T 0
caryclark@google.com0b7da432012-10-31 19:00:20 +000029#define TRY_ROTATE 1
caryclark@google.coma461ff02012-10-11 12:54:23 +000030
caryclark@google.com47580692012-07-23 12:14:49 +000031#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.com31143cf2012-11-09 22:14:19 +000032#define FORCE_RELEASE 1 // set force release to 1 for multiple thread -- no debugging
caryclark@google.comfa0588f2012-04-26 21:01:06 +000033
caryclark@google.com31143cf2012-11-09 22:14:19 +000034#if FORCE_RELEASE || defined SK_RELEASE
caryclark@google.com47580692012-07-23 12:14:49 +000035
36const bool gRunTestsInOneThread = false;
37
38#define DEBUG_ACTIVE_SPANS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000039#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000040#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000041#define DEBUG_ANGLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000042#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000043#define DEBUG_CROSS 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000044#define DEBUG_MARK_DONE 0
caryclark@google.comf839c032012-10-26 21:03:50 +000045#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com47580692012-07-23 12:14:49 +000046#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000047#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000048#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000049
50#else
51
caryclark@google.com47580692012-07-23 12:14:49 +000052const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000053
caryclark@google.comc91dfe42012-10-16 12:06:27 +000054#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000055#define DEBUG_ADD_INTERSECTING_TS 1
56#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000057#define DEBUG_ANGLE 1
58#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000059#define DEBUG_CROSS 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000060#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000061#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000062#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000063#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000064#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000065
66#endif
67
caryclark@google.com6aea33f2012-10-09 14:11:58 +000068#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000069
caryclark@google.comfa0588f2012-04-26 21:01:06 +000070#if DEBUG_DUMP
71static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000072// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000073static int gContourID;
74static int gSegmentID;
75#endif
76
caryclark@google.com8dcf1142012-07-02 20:27:02 +000077#ifndef DEBUG_TEST
78#define DEBUG_TEST 0
79#endif
80
caryclark@google.com32546db2012-08-31 20:55:07 +000081#define MAKE_CONST_LINE(line, pts) \
82 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
83#define MAKE_CONST_QUAD(quad, pts) \
84 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
85 {pts[2].fX, pts[2].fY}}
86#define MAKE_CONST_CUBIC(cubic, pts) \
87 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
88 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
89
caryclark@google.comfa0588f2012-04-26 21:01:06 +000090static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
91 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000092 MAKE_CONST_LINE(aLine, a);
93 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +000094 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
95}
96
97static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
98 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000099 MAKE_CONST_QUAD(aQuad, a);
100 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000101 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000102}
103
caryclark@google.com32546db2012-08-31 20:55:07 +0000104static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000105 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000106 MAKE_CONST_CUBIC(aCubic, a);
107 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000108 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
109}
110
111static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
112 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000113 MAKE_CONST_QUAD(aQuad, a);
114 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000115#define TRY_QUARTIC_SOLUTION 1
116#if TRY_QUARTIC_SOLUTION
117 intersect2(aQuad, bQuad, intersections);
118#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000119 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000120#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000121 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000122}
123
124static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
125 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000126 MAKE_CONST_CUBIC(aCubic, a);
127 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000128 intersect(aCubic, bCubic, intersections);
129 return intersections.fUsed;
130}
131
132static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
133 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000134 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000135 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
136}
137
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000138static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
139 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000140 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000141 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
142}
143
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000144static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
145 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000146 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000147 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
148}
149
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000150static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
151 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000152 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000153 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
154}
155
156static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
157 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000158 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000159 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
160}
161
162static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
163 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000164 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000165 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000166}
167
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000168static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
169 SkScalar , SkScalar , bool , Intersections& ) = {
170 NULL,
171 VLineIntersect,
172 VQuadIntersect,
173 VCubicIntersect
174};
175
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000176static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000177 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000178 double x, y;
179 xy_at_t(line, t, x, y);
180 out->fX = SkDoubleToScalar(x);
181 out->fY = SkDoubleToScalar(y);
182}
183
184static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000185 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000186 double x, y;
187 xy_at_t(quad, t, x, y);
188 out->fX = SkDoubleToScalar(x);
189 out->fY = SkDoubleToScalar(y);
190}
191
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000192static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
193 MAKE_CONST_QUAD(quad, a);
194 xy_at_t(quad, t, out->x, out->y);
195}
196
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000197static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000198 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000199 double x, y;
200 xy_at_t(cubic, t, x, y);
201 out->fX = SkDoubleToScalar(x);
202 out->fY = SkDoubleToScalar(y);
203}
204
205static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
206 NULL,
207 LineXYAtT,
208 QuadXYAtT,
209 CubicXYAtT
210};
211
212static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000213 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000214 double x;
215 xy_at_t(aLine, t, x, *(double*) 0);
216 return SkDoubleToScalar(x);
217}
218
219static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000220 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000221 double x;
222 xy_at_t(quad, t, x, *(double*) 0);
223 return SkDoubleToScalar(x);
224}
225
226static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000227 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000228 double x;
229 xy_at_t(cubic, t, x, *(double*) 0);
230 return SkDoubleToScalar(x);
231}
232
233static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
234 NULL,
235 LineXAtT,
236 QuadXAtT,
237 CubicXAtT
238};
239
240static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000241 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000242 double y;
243 xy_at_t(aLine, t, *(double*) 0, y);
244 return SkDoubleToScalar(y);
245}
246
247static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000248 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000249 double y;
250 xy_at_t(quad, t, *(double*) 0, y);
251 return SkDoubleToScalar(y);
252}
253
254static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000255 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000256 double y;
257 xy_at_t(cubic, t, *(double*) 0, y);
258 return SkDoubleToScalar(y);
259}
260
261static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
262 NULL,
263 LineYAtT,
264 QuadYAtT,
265 CubicYAtT
266};
267
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000268static SkScalar LineDXAtT(const SkPoint a[2], double ) {
269 return a[1].fX - a[0].fX;
270}
271
272static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000273 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000274 double x;
275 dxdy_at_t(quad, t, x, *(double*) 0);
276 return SkDoubleToScalar(x);
277}
278
279static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000280 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000281 double x;
282 dxdy_at_t(cubic, t, x, *(double*) 0);
283 return SkDoubleToScalar(x);
284}
285
286static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
287 NULL,
288 LineDXAtT,
289 QuadDXAtT,
290 CubicDXAtT
291};
292
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000293static void LineSubDivide(const SkPoint a[2], double startT, double endT,
294 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000295 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000296 _Line dst;
297 sub_divide(aLine, startT, endT, dst);
298 sub[0].fX = SkDoubleToScalar(dst[0].x);
299 sub[0].fY = SkDoubleToScalar(dst[0].y);
300 sub[1].fX = SkDoubleToScalar(dst[1].x);
301 sub[1].fY = SkDoubleToScalar(dst[1].y);
302}
303
304static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
305 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000306 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000307 Quadratic dst;
308 sub_divide(aQuad, startT, endT, dst);
309 sub[0].fX = SkDoubleToScalar(dst[0].x);
310 sub[0].fY = SkDoubleToScalar(dst[0].y);
311 sub[1].fX = SkDoubleToScalar(dst[1].x);
312 sub[1].fY = SkDoubleToScalar(dst[1].y);
313 sub[2].fX = SkDoubleToScalar(dst[2].x);
314 sub[2].fY = SkDoubleToScalar(dst[2].y);
315}
316
317static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
318 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000319 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000320 Cubic dst;
321 sub_divide(aCubic, startT, endT, dst);
322 sub[0].fX = SkDoubleToScalar(dst[0].x);
323 sub[0].fY = SkDoubleToScalar(dst[0].y);
324 sub[1].fX = SkDoubleToScalar(dst[1].x);
325 sub[1].fY = SkDoubleToScalar(dst[1].y);
326 sub[2].fX = SkDoubleToScalar(dst[2].x);
327 sub[2].fY = SkDoubleToScalar(dst[2].y);
328 sub[3].fX = SkDoubleToScalar(dst[3].x);
329 sub[3].fY = SkDoubleToScalar(dst[3].y);
330}
331
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000332static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
333 SkPoint []) = {
334 NULL,
335 LineSubDivide,
336 QuadSubDivide,
337 CubicSubDivide
338};
339
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000340static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000341 _Line sub) {
342 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000343 _Line dst;
344 sub_divide(aLine, startT, endT, dst);
345 sub[0] = dst[0];
346 sub[1] = dst[1];
347}
348
349static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000350 Quadratic sub) {
351 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000352 Quadratic dst;
353 sub_divide(aQuad, startT, endT, dst);
354 sub[0] = dst[0];
355 sub[1] = dst[1];
356 sub[2] = dst[2];
357}
358
359static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000360 Cubic sub) {
361 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000362 Cubic dst;
363 sub_divide(aCubic, startT, endT, dst);
364 sub[0] = dst[0];
365 sub[1] = dst[1];
366 sub[2] = dst[2];
367 sub[3] = dst[3];
368}
369
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000370#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000371static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
372 SkRect& bounds) {
373 SkPoint dst[3];
374 QuadSubDivide(a, startT, endT, dst);
375 bounds.fLeft = bounds.fRight = dst[0].fX;
376 bounds.fTop = bounds.fBottom = dst[0].fY;
377 for (int index = 1; index < 3; ++index) {
378 bounds.growToInclude(dst[index].fX, dst[index].fY);
379 }
380}
381
382static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
383 SkRect& bounds) {
384 SkPoint dst[4];
385 CubicSubDivide(a, startT, endT, dst);
386 bounds.fLeft = bounds.fRight = dst[0].fX;
387 bounds.fTop = bounds.fBottom = dst[0].fY;
388 for (int index = 1; index < 4; ++index) {
389 bounds.growToInclude(dst[index].fX, dst[index].fY);
390 }
391}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000392#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000393
caryclark@google.com15fa1382012-05-07 20:49:36 +0000394static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000395 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000396 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000397 Quadratic dst;
398 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000399 if (order == 2) { // quad became line
400 for (int index = 0; index < order; ++index) {
401 SkPoint* pt = reducePts.append();
402 pt->fX = SkDoubleToScalar(dst[index].x);
403 pt->fY = SkDoubleToScalar(dst[index].y);
404 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000405 }
406 return (SkPath::Verb) (order - 1);
407}
408
409static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
410 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000411 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000412 Cubic dst;
413 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000414 if (order == 2 || order == 3) { // cubic became line or quad
415 for (int index = 0; index < order; ++index) {
416 SkPoint* pt = reducePts.append();
417 pt->fX = SkDoubleToScalar(dst[index].x);
418 pt->fY = SkDoubleToScalar(dst[index].y);
419 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000420 }
421 return (SkPath::Verb) (order - 1);
422}
423
caryclark@google.com15fa1382012-05-07 20:49:36 +0000424static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000425 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000426 return isLinear(aQuad, 0, 2);
427}
428
429static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000430 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000431 return isLinear(aCubic, 0, 3);
432}
433
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000434static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000435 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000436 double x[2];
437 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000438 xy_at_t(aLine, endT, x[1], *(double*) 0);
439 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000440}
441
442static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000443 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000444 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000445}
446
447static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000448 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000449 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000450}
451
452static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
453 NULL,
454 LineLeftMost,
455 QuadLeftMost,
456 CubicLeftMost
457};
458
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000459#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000460static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
461 Intersections& intersections) {
462 MAKE_CONST_QUAD(aQuad, a);
463 MAKE_CONST_LINE(bLine, b);
464 return intersectRay(aQuad, bLine, intersections);
465}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000466#endif
467
468static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
469 Intersections& intersections) {
470 MAKE_CONST_QUAD(aQuad, a);
471 return intersectRay(aQuad, bLine, intersections);
472}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000473
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000474class Segment;
475
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000476struct Span {
477 Segment* fOther;
478 mutable SkPoint fPt; // lazily computed as needed
479 double fT;
480 double fOtherT; // value at fOther[fOtherIndex].fT
481 int fOtherIndex; // can't be used during intersection
caryclark@google.com31143cf2012-11-09 22:14:19 +0000482 int fWindSum; // accumulated from contours surrounding this one.
483 int fOppSum; // for binary operators: the opposite winding sum
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000484 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000485 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000486 bool fUnsortableStart; // set when start is part of an unsortable pair
487 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.comf839c032012-10-26 21:03:50 +0000488 bool fTiny; // if set, span may still be considered once for edge following
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000489};
490
caryclark@google.com15fa1382012-05-07 20:49:36 +0000491// sorting angles
492// given angles of {dx dy ddx ddy dddx dddy} sort them
493class Angle {
494public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000495 // FIXME: this is bogus for quads and cubics
496 // if the quads and cubics' line from end pt to ctrl pt are coincident,
497 // there's no obvious way to determine the curve ordering from the
498 // derivatives alone. In particular, if one quadratic's coincident tangent
499 // is longer than the other curve, the final control point can place the
500 // longer curve on either side of the shorter one.
501 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
502 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000503
caryclark@google.com32546db2012-08-31 20:55:07 +0000504 /*(
505 for quads and cubics, set up a parameterized line (e.g. LineParameters )
506 for points [0] to [1]. See if point [2] is on that line, or on one side
507 or the other. If it both quads' end points are on the same side, choose
508 the shorter tangent. If the tangents are equal, choose the better second
509 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000510
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000511 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000512 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000513 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000514 double y = dy();
515 double ry = rh.dy();
516 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
517 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000518 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000519 double x = dx();
520 double rx = rh.dx();
521 if (y == 0 && ry == 0 && x * rx < 0) {
522 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000523 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000524 double x_ry = x * ry;
525 double rx_y = rx * y;
526 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000527 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000528 return cmp < 0;
529 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000530 if (approximately_zero(x_ry) && approximately_zero(rx_y)
531 && !approximately_zero_squared(cmp)) {
532 return cmp < 0;
533 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000534 // at this point, the initial tangent line is coincident
caryclark@google.com31143cf2012-11-09 22:14:19 +0000535 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
536 || !approximately_zero(rh.fSide))) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000537 // FIXME: running demo will trigger this assertion
538 // (don't know if commenting out will trigger further assertion or not)
539 // commenting it out allows demo to run in release, though
540 // SkASSERT(fSide != rh.fSide);
541 return fSide < rh.fSide;
542 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000543 // see if either curve can be lengthened and try the tangent compare again
544 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
545 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
546 Angle longer = *this;
547 Angle rhLonger = rh;
548 if (longer.lengthen() | rhLonger.lengthen()) {
549 return longer < rhLonger;
550 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000551 // what if we extend in the other direction?
552 longer = *this;
553 rhLonger = rh;
554 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
555 return longer < rhLonger;
556 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000557 }
caryclark@google.com185c7c42012-10-19 18:26:24 +0000558 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
caryclark@google.com31143cf2012-11-09 22:14:19 +0000559 || (rh.fVerb == SkPath::kLine_Verb
560 && approximately_zero(rx) && approximately_zero(ry))) {
caryclark@google.com185c7c42012-10-19 18:26:24 +0000561 // See general unsortable comment below. This case can happen when
562 // one line has a non-zero change in t but no change in x and y.
563 fUnsortable = true;
564 rh.fUnsortable = true;
565 return this < &rh; // even with no solution, return a stable sort
566 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000567 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
568 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000569 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000570 // end of the shorter tangent to midway between the end points
571 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000572 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
573 double len = fTangent1.normalSquared();
574 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000575 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000576 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000577 int roots, rroots;
578 bool flip = false;
579 do {
580 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
581 double midX = (q[0].x + q[2].x) / 2;
582 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000583 ray[0] = q[1];
584 ray[1].x = midX;
585 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000586 SkASSERT(ray[0] != ray[1]);
587 roots = QuadRayIntersect(fPts, ray, i);
588 rroots = QuadRayIntersect(rh.fPts, ray, ri);
589 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000590 if (roots == 0 || rroots == 0) {
591 // FIXME: we don't have a solution in this case. The interim solution
592 // is to mark the edges as unsortable, exclude them from this and
593 // future computations, and allow the returned path to be fragmented
594 fUnsortable = true;
595 rh.fUnsortable = true;
596 return this < &rh; // even with no solution, return a stable sort
597 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000598 _Point loc;
599 double best = SK_ScalarInfinity;
600 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000601 int index;
602 for (index = 0; index < roots; ++index) {
603 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000604 dx = loc.x - ray[0].x;
605 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000606 dist = dx * dx + dy * dy;
607 if (best > dist) {
608 best = dist;
609 }
610 }
611 for (index = 0; index < rroots; ++index) {
612 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000613 dx = loc.x - ray[0].x;
614 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000615 dist = dx * dx + dy * dy;
616 if (best > dist) {
617 return fSide < 0;
618 }
619 }
620 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000621 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000622
caryclark@google.com47580692012-07-23 12:14:49 +0000623 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000624 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000625 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000626
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000627 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000628 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000629 }
630
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000631 int end() const {
632 return fEnd;
633 }
634
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000635 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000636 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000637 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000638
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000639 bool lengthen() {
640 int newEnd = fEnd;
641 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
642 fEnd = newEnd;
643 setSpans();
644 return true;
645 }
646 return false;
647 }
648
caryclark@google.coma461ff02012-10-11 12:54:23 +0000649 bool reverseLengthen() {
650 if (fReversed) {
651 return false;
652 }
653 int newEnd = fStart;
654 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
655 fEnd = newEnd;
656 fReversed = true;
657 setSpans();
658 return true;
659 }
660 return false;
661 }
662
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000663 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000664 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000665 fSegment = segment;
666 fStart = start;
667 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000668 fPts = orig;
669 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000670 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000671 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000672 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000673 setSpans();
674 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000675
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000676 void setSpans() {
677 double startT = (*fSpans)[fStart].fT;
678 double endT = (*fSpans)[fEnd].fT;
679 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000680 case SkPath::kLine_Verb:
681 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000682 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000683 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
684 fTangent1.lineEndPoints(l);
caryclark@google.comf839c032012-10-26 21:03:50 +0000685 fUnsortable = dx() == 0 && dy() == 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000686 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000687 break;
688 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000689 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000690 fTangent1.quadEndPoints(fQ, 0, 1);
691 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000692 break;
693 case SkPath::kCubic_Verb:
694 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000695 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000696 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000697 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000698 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000699 default:
700 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000701 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000702 if (fUnsortable) {
703 return;
704 }
705 SkASSERT(fStart != fEnd);
706 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
707 for (int index = fStart; index != fEnd; index += step) {
708 if ((*fSpans)[index].fUnsortableStart) {
709 fUnsortable = true;
710 return;
711 }
712 if (index != fStart && (*fSpans)[index].fUnsortableEnd) {
713 fUnsortable = true;
714 return;
715 }
716 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000717 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000718
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000719 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000720 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000721 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000722
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000723 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000724 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000725 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000726
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000727 const SkTDArray<Span>* spans() const {
728 return fSpans;
729 }
730
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000731 int start() const {
732 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000733 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000734
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000735 bool unsortable() const {
736 return fUnsortable;
737 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000738
caryclark@google.comc899ad92012-08-23 15:24:42 +0000739#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000740 const SkPoint* pts() const {
741 return fPts;
742 }
743
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000744 SkPath::Verb verb() const {
745 return fVerb;
746 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000747
caryclark@google.comc899ad92012-08-23 15:24:42 +0000748 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000749 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000750 }
751#endif
752
caryclark@google.com15fa1382012-05-07 20:49:36 +0000753private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000754 const SkPoint* fPts;
755 Quadratic fQ;
756 SkPath::Verb fVerb;
757 double fSide;
758 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000759 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000760 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000761 int fStart;
762 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000763 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000764 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000765};
766
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000767// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000768struct Bounds : public SkRect {
769 static bool Intersects(const Bounds& a, const Bounds& b) {
770 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
771 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
772 }
773
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000774 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
775 if (left < fLeft) {
776 fLeft = left;
777 }
778 if (top < fTop) {
779 fTop = top;
780 }
781 if (right > fRight) {
782 fRight = right;
783 }
784 if (bottom > fBottom) {
785 fBottom = bottom;
786 }
787 }
788
789 void add(const Bounds& toAdd) {
790 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
791 }
792
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000793 bool isEmpty() {
794 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000795 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000796 || isnan(fLeft) || isnan(fRight)
797 || isnan(fTop) || isnan(fBottom);
798 }
799
800 void setCubicBounds(const SkPoint a[4]) {
801 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000802 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000803 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000804 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
805 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000806 }
807
808 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000809 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000810 _Rect dRect;
811 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000812 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
813 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000814 }
815};
816
caryclark@google.com2ddff932012-08-07 21:25:27 +0000817static bool useInnerWinding(int outerWinding, int innerWinding) {
818 SkASSERT(outerWinding != innerWinding);
819 int absOut = abs(outerWinding);
820 int absIn = abs(innerWinding);
821 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
822 if (outerWinding * innerWinding < 0) {
823#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000824 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000825 outerWinding, innerWinding, result ? "true" : "false");
826#endif
827 }
828 return result;
829}
830
caryclark@google.com31143cf2012-11-09 22:14:19 +0000831static const bool gOpLookup[][2][2] = {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000832 // ==0 !=0
833 // b a b a
834 {{true , false}, {false, true }}, // a - b
835 {{false, false}, {true , true }}, // a & b
836 {{true , true }, {false, false}}, // a | b
837 {{true , true }, {true , true }}, // a ^ b
838};
839
840static bool activeOp(bool angleIsOp, int otherNonZero, ShapeOp op) {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000841 return gOpLookup[op][otherNonZero][angleIsOp];
caryclark@google.com235f56a2012-09-14 14:19:30 +0000842}
843
caryclark@google.comf839c032012-10-26 21:03:50 +0000844// wrap path to keep track of whether the contour is initialized and non-empty
845class PathWrapper {
846public:
847 PathWrapper(SkPath& path)
848 : fPathPtr(&path)
849 {
850 init();
851 }
852
853 void close() {
854 if (!fHasMove) {
855 return;
856 }
857 bool callClose = isClosed();
858 lineTo();
859 if (fEmpty) {
860 return;
861 }
862 if (callClose) {
863 #if DEBUG_PATH_CONSTRUCTION
864 SkDebugf("path.close();\n");
865 #endif
866 fPathPtr->close();
867 }
868 init();
869 }
870
871 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
872 lineTo();
873 moveTo();
874#if DEBUG_PATH_CONSTRUCTION
875 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
876 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
877#endif
878 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
879 fDefer[0] = fDefer[1] = pt3;
880 fEmpty = false;
881 }
882
883 void deferredLine(const SkPoint& pt) {
884 if (pt == fDefer[1]) {
885 return;
886 }
887 if (changedSlopes(pt)) {
888 lineTo();
889 fDefer[0] = fDefer[1];
890 }
891 fDefer[1] = pt;
892 }
893
894 void deferredMove(const SkPoint& pt) {
895 fMoved = true;
896 fHasMove = true;
897 fEmpty = true;
898 fDefer[0] = fDefer[1] = pt;
899 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000900
caryclark@google.comf839c032012-10-26 21:03:50 +0000901 void deferredMoveLine(const SkPoint& pt) {
902 if (!fHasMove) {
903 deferredMove(pt);
904 }
905 deferredLine(pt);
906 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000907
caryclark@google.comf839c032012-10-26 21:03:50 +0000908 bool hasMove() const {
909 return fHasMove;
910 }
911
912 void init() {
913 fEmpty = true;
914 fHasMove = false;
915 fMoved = false;
916 }
917
918 bool isClosed() const {
919 return !fEmpty && fFirstPt == fDefer[1];
920 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000921
caryclark@google.comf839c032012-10-26 21:03:50 +0000922 void lineTo() {
923 if (fDefer[0] == fDefer[1]) {
924 return;
925 }
926 moveTo();
927 fEmpty = false;
928#if DEBUG_PATH_CONSTRUCTION
929 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
930#endif
931 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
932 fDefer[0] = fDefer[1];
933 }
934
935 const SkPath* nativePath() const {
936 return fPathPtr;
937 }
938
939 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
940 lineTo();
941 moveTo();
942#if DEBUG_PATH_CONSTRUCTION
943 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
944 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
945#endif
946 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
947 fDefer[0] = fDefer[1] = pt2;
948 fEmpty = false;
949 }
950
951protected:
952 bool changedSlopes(const SkPoint& pt) const {
953 if (fDefer[0] == fDefer[1]) {
954 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000955 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000956 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
957 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
958 SkScalar lineDx = pt.fX - fDefer[1].fX;
959 SkScalar lineDy = pt.fY - fDefer[1].fY;
960 return deferDx * lineDy != deferDy * lineDx;
961 }
962
963 void moveTo() {
964 if (!fMoved) {
965 return;
966 }
967 fFirstPt = fDefer[0];
968#if DEBUG_PATH_CONSTRUCTION
969 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
970#endif
971 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
972 fMoved = false;
973 }
974
975private:
976 SkPath* fPathPtr;
977 SkPoint fDefer[2];
978 SkPoint fFirstPt;
979 bool fEmpty;
980 bool fHasMove;
981 bool fMoved;
982};
983
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000984class Segment {
985public:
986 Segment() {
987#if DEBUG_DUMP
988 fID = ++gSegmentID;
989#endif
990 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +0000991
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000992 bool operator<(const Segment& rh) const {
993 return fBounds.fTop < rh.fBounds.fTop;
994 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000995
caryclark@google.com9764cc62012-07-12 19:29:45 +0000996 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
997 if (activeAngleInner(index, done, angles)) {
998 return true;
999 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001000 double referenceT = fTs[index].fT;
1001 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001002 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001003 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001004 return true;
1005 }
1006 }
1007 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001008 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001009 return true;
1010 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001011 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001012 return false;
1013 }
1014
caryclark@google.com9764cc62012-07-12 19:29:45 +00001015 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001016 Span* span = &fTs[index];
1017 Segment* other = span->fOther;
1018 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001019 return other->activeAngleInner(oIndex, done, angles);
1020 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001021
caryclark@google.com9764cc62012-07-12 19:29:45 +00001022 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001023 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001024 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001025 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001026 if (upSpan.fWindValue) {
1027 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001028 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001029 done++;
1030 } else if (upSpan.fWindSum != SK_MinS32) {
1031 return true;
1032 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001033 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001034 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001035 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001036 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001037 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001038 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001039 if (downSpan.fWindValue) {
1040 addAngle(angles, index, prev);
1041 if (downSpan.fDone) {
1042 done++;
1043 } else if (downSpan.fWindSum != SK_MinS32) {
1044 return true;
1045 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001046 }
1047 }
1048 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001049 }
1050
caryclark@google.comf839c032012-10-26 21:03:50 +00001051 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001052 SkASSERT(!done());
1053 int count = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001054 result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001055 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001056 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001057 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001058 const Span& span = fTs[index];
1059 if (span.fUnsortableStart | lastUnsortable) {
1060 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001061 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001062 if (!span.fDone | !lastDone) {
1063 const SkPoint& xy = xyAtT(index);
1064 if (result.fY < xy.fY) {
1065 goto next;
1066 }
1067 if (result.fY == xy.fY && result.fX < xy.fX) {
1068 goto next;
1069 }
1070 result = xy;
1071 }
1072 next:
1073 lastDone = span.fDone;
1074 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001075 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001076 SkASSERT(result.fY < SK_ScalarMax);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001077 }
1078
1079 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001080 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001081 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001082#if DEBUG_ANGLE
caryclark@google.com31143cf2012-11-09 22:14:19 +00001083 if (angles.count() > 1 && !fTs[start].fTiny) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001084 SkPoint angle0Pt, newPt;
1085 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1086 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1087 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1088 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1089 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1090 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001091#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001092 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001093 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001094
caryclark@google.com2ddff932012-08-07 21:25:27 +00001095 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001096 double oEnd) {
1097 int tIndex = -1;
1098 int tCount = fTs.count();
1099 int oIndex = -1;
1100 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001101 do {
1102 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001103 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001104 int tIndexStart = tIndex;
1105 do {
1106 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001107 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001108 int oIndexStart = oIndex;
1109 double nextT;
1110 do {
1111 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001112 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001113 double oNextT;
1114 do {
1115 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001116 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001117 // at this point, spans before and after are at:
1118 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1119 // if tIndexStart == 0, no prior span
1120 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001121
caryclark@google.comcc905052012-07-25 20:59:42 +00001122 // advance the span with zero winding
1123 // if the following span exists (not past the end, non-zero winding)
1124 // connect the two edges
1125 if (!fTs[tIndexStart].fWindValue) {
1126 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1127 #if DEBUG_CONCIDENT
1128 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1129 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001130 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1131 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001132 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001133 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001134 }
1135 if (nextT < 1 && fTs[tIndex].fWindValue) {
1136 #if DEBUG_CONCIDENT
1137 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1138 __FUNCTION__, fID, other.fID, tIndex,
1139 fTs[tIndex].fT, xyAtT(tIndex).fX,
1140 xyAtT(tIndex).fY);
1141 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001142 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001143 }
1144 } else {
1145 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1146 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1147 #if DEBUG_CONCIDENT
1148 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1149 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001150 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1151 other.xyAtT(oIndexStart).fY);
1152 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001153 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001154 }
1155 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1156 #if DEBUG_CONCIDENT
1157 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1158 __FUNCTION__, fID, other.fID, oIndex,
1159 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1160 other.xyAtT(oIndex).fY);
1161 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1162 #endif
1163 }
1164 }
1165 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001166
caryclark@google.comcc905052012-07-25 20:59:42 +00001167 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1168 double oEnd) {
1169 // walk this to outsideTs[0]
1170 // walk other to outsideTs[1]
1171 // if either is > 0, add a pointer to the other, copying adjacent winding
1172 int tIndex = -1;
1173 int oIndex = -1;
1174 double tStart = outsideTs[0];
1175 double oStart = outsideTs[1];
1176 do {
1177 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001178 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001179 do {
1180 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001181 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001182 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001183 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001184 }
1185 tStart = fTs[tIndex].fT;
1186 oStart = other.fTs[oIndex].fT;
1187 do {
1188 double nextT;
1189 do {
1190 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001191 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001192 tStart = nextT;
1193 do {
1194 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001195 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001196 oStart = nextT;
1197 if (tStart == 1 && oStart == 1) {
1198 break;
1199 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001200 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001201 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001202 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001203
caryclark@google.com235f56a2012-09-14 14:19:30 +00001204 void addCubic(const SkPoint pts[4], bool operand) {
1205 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001206 fBounds.setCubicBounds(pts);
1207 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001208
caryclark@google.comf839c032012-10-26 21:03:50 +00001209 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001210 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001211 const SkPoint* ePtr;
1212 int lastT = fTs.count() - 1;
1213 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1214 ePtr = fPts;
1215 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001216 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001217 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1218 ePtr = edge;
1219 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001220 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001221 bool reverse = ePtr == fPts && start != 0;
1222 if (reverse) {
1223 path.deferredMoveLine(ePtr[fVerb]);
1224 switch (fVerb) {
1225 case SkPath::kLine_Verb:
1226 path.deferredLine(ePtr[0]);
1227 break;
1228 case SkPath::kQuad_Verb:
1229 path.quadTo(ePtr[1], ePtr[0]);
1230 break;
1231 case SkPath::kCubic_Verb:
1232 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1233 break;
1234 default:
1235 SkASSERT(0);
1236 }
1237 // return ePtr[0];
1238 } else {
1239 path.deferredMoveLine(ePtr[0]);
1240 switch (fVerb) {
1241 case SkPath::kLine_Verb:
1242 path.deferredLine(ePtr[1]);
1243 break;
1244 case SkPath::kQuad_Verb:
1245 path.quadTo(ePtr[1], ePtr[2]);
1246 break;
1247 case SkPath::kCubic_Verb:
1248 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1249 break;
1250 default:
1251 SkASSERT(0);
1252 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001253 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001254 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001255 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001256 }
1257
caryclark@google.com235f56a2012-09-14 14:19:30 +00001258 void addLine(const SkPoint pts[2], bool operand) {
1259 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001260 fBounds.set(pts, 2);
1261 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001262
caryclark@google.comf839c032012-10-26 21:03:50 +00001263#if 0
1264 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001265 const SkPoint& pt = xyAtT(tIndex);
1266 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001267 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001268 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001269 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001270 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001271#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001272
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001273 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001274 void addOtherT(int index, double otherT, int otherIndex) {
1275 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001276 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001277 if (precisely_less_than_zero(otherT)) {
1278 otherT = 0;
1279 } else if (precisely_greater_than_one(otherT)) {
1280 otherT = 1;
1281 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001282 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001283 span.fOtherT = otherT;
1284 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001285 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001286
caryclark@google.com235f56a2012-09-14 14:19:30 +00001287 void addQuad(const SkPoint pts[3], bool operand) {
1288 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001289 fBounds.setQuadBounds(pts);
1290 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001291
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001292 // Defer all coincident edge processing until
1293 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001294
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001295// no need to be tricky; insert in normal T order
1296// resolve overlapping ts when considering coincidence later
1297
1298 // add non-coincident intersection. Resulting edges are sorted in T.
1299 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001300 // FIXME: in the pathological case where there is a ton of intercepts,
1301 // binary search?
1302 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001303 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001304 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001305 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001306 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001307 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001308 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001309 newT = 1;
1310 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001311 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001312 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001313 // OPTIMIZATION: if there are three or more identical Ts, then
1314 // the fourth and following could be further insertion-sorted so
1315 // that all the edges are clockwise or counterclockwise.
1316 // This could later limit segment tests to the two adjacent
1317 // neighbors, although it doesn't help with determining which
1318 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001319 if (newT < fTs[index].fT) {
1320 insertedAt = index;
1321 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001322 }
1323 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001324 Span* span;
1325 if (insertedAt >= 0) {
1326 span = fTs.insert(insertedAt);
1327 } else {
1328 insertedAt = tCount;
1329 span = fTs.append();
1330 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001331 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001332 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001333 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001334 span->fWindSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001335 span->fOppSum = SK_MinS32;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001336 span->fWindValue = 1;
caryclark@google.comf839c032012-10-26 21:03:50 +00001337 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001338 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001339 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001340 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001341 span->fUnsortableStart = false;
1342 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001343 if (span - fTs.begin() > 0 && !span[-1].fDone
1344 && !precisely_negative(newT - span[-1].fT)
1345 // && approximately_negative(newT - span[-1].fT)
1346 && xyAtT(&span[-1]) == xyAtT(span)) {
1347 span[-1].fTiny = true;
1348 span[-1].fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001349 if (approximately_negative(newT - span[-1].fT)) {
1350 if (approximately_greater_than_one(newT)) {
1351 span[-1].fUnsortableStart = true;
1352 span[-2].fUnsortableEnd = true;
1353 }
1354 if (approximately_less_than_zero(span[-1].fT)) {
1355 span->fUnsortableStart = true;
1356 span[-1].fUnsortableEnd = true;
1357 }
1358 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001359 ++fDoneSpans;
1360 }
1361 if (fTs.end() - span > 1 && !span->fDone
1362 && !precisely_negative(span[1].fT - newT)
1363 // && approximately_negative(span[1].fT - newT)
1364 && xyAtT(&span[1]) == xyAtT(span)) {
1365 span->fTiny = true;
1366 span->fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001367 if (approximately_negative(span[1].fT - newT)) {
1368 if (approximately_greater_than_one(span[1].fT)) {
1369 span->fUnsortableStart = true;
1370 span[-1].fUnsortableEnd = true;
1371 }
1372 if (approximately_less_than_zero(newT)) {
1373 span[1].fUnsortableStart = true;
1374 span->fUnsortableEnd = true;
1375 }
1376 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001377 ++fDoneSpans;
1378 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001379 return insertedAt;
1380 }
1381
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001382 // set spans from start to end to decrement by one
1383 // note this walks other backwards
1384 // FIMXE: there's probably an edge case that can be constructed where
1385 // two span in one segment are separated by float epsilon on one span but
1386 // not the other, if one segment is very small. For this
1387 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001388 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001389 // sorted? It feels better in such a case to match the span's other span
1390 // pointer since both coincident segments must contain the same spans.
1391 void addTCancel(double startT, double endT, Segment& other,
1392 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001393 SkASSERT(!approximately_negative(endT - startT));
1394 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001395 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001396 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001397 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001398 ++index;
1399 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001400 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001401 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001402 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001403 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001404 Span* test = &fTs[index];
1405 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001406 SkTDArray<double> outsideTs;
1407 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001408 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001409 bool decrement = test->fWindValue && oTest->fWindValue && !binary;
caryclark@google.comcc905052012-07-25 20:59:42 +00001410 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001411 double testT = test->fT;
1412 double oTestT = oTest->fT;
1413 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001414 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001415 if (decrement) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001416 decrementSpan(span);
caryclark@google.com200c2112012-08-03 15:05:04 +00001417 } else if (track && span->fT < 1 && oTestT < 1) {
1418 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001419 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001420 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001421 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001422 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001423 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1424 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1425 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001426 while (approximately_negative(otherTMatchStart - oSpan->fT)
1427 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001428 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001429 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001430 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001431 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001432 other.decrementSpan(oSpan);
1433 } else if (track && oSpan->fT < 1 && testT < 1) {
1434 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001435 }
1436 if (!oIndex) {
1437 break;
1438 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001439 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001440 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001441 test = span;
1442 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001443 } while (!approximately_negative(endT - test->fT));
1444 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001445 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001446 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001447 double tStart = outsideTs[0];
1448 double oStart = outsideTs[1];
1449 addCancelOutsides(tStart, oStart, other, oEndT);
1450 int count = outsideTs.count();
1451 if (count > 2) {
1452 double tStart = outsideTs[count - 2];
1453 double oStart = outsideTs[count - 1];
1454 addCancelOutsides(tStart, oStart, other, oEndT);
1455 }
caryclark@google.com18063442012-07-25 12:05:18 +00001456 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001457 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001458 double tStart = oOutsideTs[0];
1459 double oStart = oOutsideTs[1];
1460 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001461 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001462 }
1463
1464 // set spans from start to end to increment the greater by one and decrement
1465 // the lesser
caryclark@google.com31143cf2012-11-09 22:14:19 +00001466 void addTCoincident(bool isXor, double startT, double endT,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001467 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001468 SkASSERT(!approximately_negative(endT - startT));
1469 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00001470 isXor |= fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001471 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001472 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001473 ++index;
1474 }
1475 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001476 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001477 ++oIndex;
1478 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001479 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001480 Span* test = &fTs[index];
1481 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001482 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001483 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001484 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001485 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001486 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001487 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001488 bool decrementThis = (test->fWindValue < oTest->fWindValue) & !isXor;
1489 bool decrementOther = (test->fWindValue >= oTest->fWindValue) & !isXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001490 Span* end = test;
1491 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001492 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001493 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001494 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001495 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001496 if (transfer) {
1497 if (decrementOther) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001498 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001499 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001500 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00001501 ++(end->fWindValue);
caryclark@google.com18063442012-07-25 12:05:18 +00001502 } else if (decrementSpan(end)) {
1503 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001504 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001505 } else if (oTest->fWindValue) {
1506 SkASSERT(!decrementOther);
1507 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1508 TrackOutside(xOutsideTs, end->fT, oStartT);
1509 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001510 }
1511 end = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001512 } while (approximately_negative(end->fT - test->fT));
caryclark@google.com59823f72012-08-09 18:17:47 +00001513 // because of the order in which coincidences are resolved, this and other
1514 // may not have the same intermediate points. Compute the corresponding
1515 // intermediate T values (using this as the master, other as the follower)
1516 // and walk other conditionally -- hoping that it catches up in the end
1517 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001518 Span* oEnd = oTest;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001519 while (!approximately_negative(oEndT - oEnd->fT)
1520 && approximately_negative(oEnd->fT - otherTMatch)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00001521 if (transfer) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001522 if (decrementThis) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001523 #ifdef SK_DEBUG
1524 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1525 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00001526 ++(oEnd->fWindValue);
caryclark@google.com18063442012-07-25 12:05:18 +00001527 } else if (other.decrementSpan(oEnd)) {
1528 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001529 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001530 } else if (test->fWindValue) {
1531 SkASSERT(!decrementOther);
1532 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1533 SkASSERT(0); // track for later?
1534 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001535 }
1536 oEnd = &other.fTs[++oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001537 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001538 test = end;
1539 oTest = oEnd;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001540 } while (!approximately_negative(endT - test->fT));
1541 SkASSERT(approximately_negative(oTest->fT - oEndT));
1542 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001543 if (!done()) {
1544 if (outsideTs.count()) {
1545 addCoinOutsides(outsideTs, other, oEndT);
1546 }
1547 if (xOutsideTs.count()) {
1548 addCoinOutsides(xOutsideTs, other, oEndT);
1549 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001550 }
1551 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001552 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001553 }
1554 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001555
caryclark@google.comcc905052012-07-25 20:59:42 +00001556 // FIXME: this doesn't prevent the same span from being added twice
1557 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001558 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001559 int tCount = fTs.count();
1560 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1561 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001562 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001563 break;
1564 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001565 if (approximately_negative(span.fT - t) && span.fOther == &other
1566 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001567#if DEBUG_ADD_T_PAIR
1568 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1569 __FUNCTION__, fID, t, other.fID, otherT);
1570#endif
1571 return;
1572 }
1573 }
caryclark@google.com47580692012-07-23 12:14:49 +00001574#if DEBUG_ADD_T_PAIR
1575 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1576 __FUNCTION__, fID, t, other.fID, otherT);
1577#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001578 int insertedAt = addT(t, &other);
1579 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001580 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001581 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001582 matchWindingValue(insertedAt, t, borrowWind);
1583 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001584 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001585
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001586 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001587 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001588 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1589 addAngle(angles, end, start);
1590 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001591 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001592 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001593 int tIndex = nextExactSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001594 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001595 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001596 }
1597 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001598
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001599 const Bounds& bounds() const {
1600 return fBounds;
1601 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001602
caryclark@google.com31143cf2012-11-09 22:14:19 +00001603 void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001604 double referenceT = fTs[index].fT;
1605 int lesser = index;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001606 while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand)
1607 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00001608 buildAnglesInner(lesser, angles);
1609 }
1610 do {
1611 buildAnglesInner(index, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001612 } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand)
1613 && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001614 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001615
1616 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1617 Span* span = &fTs[index];
1618 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001619 // if there is only one live crossing, and no coincidence, continue
1620 // in the same direction
1621 // if there is coincidence, the only choice may be to reverse direction
1622 // find edge on either side of intersection
1623 int oIndex = span->fOtherIndex;
1624 // if done == -1, prior span has already been processed
1625 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001626 int next = other->nextExactSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001627 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001628 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001629 next = other->nextExactSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001630 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001631 // add candidate into and away from junction
1632 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001633 }
1634
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001635 int computeSum(int startIndex, int endIndex) {
1636 SkTDArray<Angle> angles;
1637 addTwoAngles(startIndex, endIndex, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001638 buildAngles(endIndex, angles, false);
caryclark@google.comd1688742012-09-18 20:08:37 +00001639 // OPTIMIZATION: check all angles to see if any have computed wind sum
1640 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001641 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001642 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001643#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001644 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001645#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001646 if (!sortable) {
1647 return SK_MinS32;
1648 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001649 int angleCount = angles.count();
1650 const Angle* angle;
1651 const Segment* base;
1652 int winding;
1653 int firstIndex = 0;
1654 do {
1655 angle = sorted[firstIndex];
1656 base = angle->segment();
1657 winding = base->windSum(angle);
1658 if (winding != SK_MinS32) {
1659 break;
1660 }
1661 if (++firstIndex == angleCount) {
1662 return SK_MinS32;
1663 }
1664 } while (true);
1665 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001666 int spanWinding = base->spanSign(angle);
1667 bool inner = useInnerWinding(winding + spanWinding, winding);
1668 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001669 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001670 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001671 inner ? winding + spanWinding : winding);
1672 #endif
1673 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001674 winding += spanWinding;
1675 }
1676 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001677 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001678 #endif
1679 int nextIndex = firstIndex + 1;
1680 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001681 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001682 do {
1683 if (nextIndex == angleCount) {
1684 nextIndex = 0;
1685 }
1686 angle = sorted[nextIndex];
1687 Segment* segment = angle->segment();
1688 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001689 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001690 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001691 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001692 maxWinding = winding;
1693 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001694 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001695 }
1696 } while (++nextIndex != lastIndex);
1697 return windSum(SkMin32(startIndex, endIndex));
1698 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001699
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001700 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001701 int bestT = -1;
1702 SkScalar top = bounds().fTop;
1703 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001704 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001705 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001706 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001707 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001708 if (fTs[start].fWindValue == 0) {
1709 continue;
1710 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001711 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001712 double startT = fTs[start].fT;
1713 double endT = fTs[end].fT;
1714 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001715 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001716 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001717 // FIXME: always use original and limit results to T values within
1718 // start t and end t.
1719 // OPTIMIZE: use specialty function that intersects ray with curve,
1720 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001721 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1722 false, intersections);
1723 if (pts == 0) {
1724 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001725 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001726 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1727 // if the intersection is edge on, wait for another one
1728 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001729 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001730 for (int index = 0; index < pts; ++index) {
1731 SkPoint pt;
1732 double foundT = intersections.fT[0][index];
1733 double testT = startT + (endT - startT) * foundT;
1734 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1735 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001736 if (fVerb > SkPath::kLine_Verb
1737 && !approximately_less_than_zero(foundT)
1738 && !approximately_greater_than_one(foundT)) {
1739 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1740 if (approximately_zero(dx)) {
1741 continue;
1742 }
1743 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001744 bestY = pt.fY;
1745 bestT = foundT < 1 ? start : end;
1746 hitT = testT;
1747 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001748 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001749 } while (fTs[end].fT != 1);
1750 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001751 }
caryclark@google.com18063442012-07-25 12:05:18 +00001752
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001753 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1754 // if a segment is connected to this one, consider it crossing
1755 int tIndex;
1756 if (fPts[0].fX == basePt.fX) {
1757 tIndex = 0;
1758 do {
1759 const Span& sSpan = fTs[tIndex];
1760 const Segment* sOther = sSpan.fOther;
1761 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1762 continue;
1763 }
1764 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1765 : sOther->fBounds.fRight > basePt.fX) {
1766 return true;
1767 }
1768 } while (fTs[++tIndex].fT == 0);
1769 }
1770 if (fPts[fVerb].fX == basePt.fX) {
1771 tIndex = fTs.count() - 1;
1772 do {
1773 const Span& eSpan = fTs[tIndex];
1774 const Segment* eOther = eSpan.fOther;
1775 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1776 continue;
1777 }
1778 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1779 : eOther->fBounds.fRight > basePt.fX) {
1780 return true;
1781 }
1782 } while (fTs[--tIndex].fT == 1);
1783 }
1784 return false;
1785 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001786
caryclark@google.com18063442012-07-25 12:05:18 +00001787 bool decrementSpan(Span* span) {
1788 SkASSERT(span->fWindValue > 0);
1789 if (--(span->fWindValue) == 0) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001790 if (!span->fDone) {
1791 span->fDone = true;
1792 ++fDoneSpans;
1793 }
caryclark@google.com18063442012-07-25 12:05:18 +00001794 return true;
1795 }
1796 return false;
1797 }
1798
caryclark@google.com15fa1382012-05-07 20:49:36 +00001799 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001800 SkASSERT(fDoneSpans <= fTs.count());
1801 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001802 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001803
caryclark@google.comf839c032012-10-26 21:03:50 +00001804 bool done(int min) const {
1805 return fTs[min].fDone;
1806 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001807
caryclark@google.com47580692012-07-23 12:14:49 +00001808 bool done(const Angle& angle) const {
caryclark@google.comf839c032012-10-26 21:03:50 +00001809 return done(SkMin32(angle.start(), angle.end()));
caryclark@google.com47580692012-07-23 12:14:49 +00001810 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001811
caryclark@google.com235f56a2012-09-14 14:19:30 +00001812 Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
caryclark@google.com31143cf2012-11-09 22:14:19 +00001813 int& nextStart, int& nextEnd, int& winding, int& oppWinding,
1814 int& spanWinding, bool& unsortable, ShapeOp op,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001815 const int aXorMask, const int bXorMask) {
1816 const int startIndex = nextStart;
1817 const int endIndex = nextEnd;
1818 int outerWinding = winding;
1819 int innerWinding = winding + spanWinding;
1820 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00001821 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d oppWinding=%d\n",
1822 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001823 #endif
1824 if (useInnerWinding(outerWinding, innerWinding)) {
1825 outerWinding = innerWinding;
1826 }
1827 SkASSERT(startIndex != endIndex);
1828 int count = fTs.count();
1829 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1830 : startIndex > 0);
1831 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001832 int end = nextExactSpan(startIndex, step);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001833 SkASSERT(end >= 0);
1834 Span* endSpan = &fTs[end];
1835 Segment* other;
1836 if (isSimple(end)) {
1837 // mark the smaller of startIndex, endIndex done, and all adjacent
1838 // spans with the same T value (but not 'other' spans)
1839 #if DEBUG_WINDING
1840 SkDebugf("%s simple\n", __FUNCTION__);
1841 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00001842 markDone(SkMin32(startIndex, endIndex), outerWinding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001843 other = endSpan->fOther;
1844 nextStart = endSpan->fOtherIndex;
1845 double startT = other->fTs[nextStart].fT;
1846 nextEnd = nextStart;
1847 do {
1848 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001849 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00001850 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001851 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1852 return other;
1853 }
1854 // more than one viable candidate -- measure angles to find best
1855 SkTDArray<Angle> angles;
1856 SkASSERT(startIndex - endIndex != 0);
1857 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1858 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001859 buildAngles(end, angles, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001860 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001861 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001862 int angleCount = angles.count();
1863 int firstIndex = findStartingEdge(sorted, startIndex, end);
1864 SkASSERT(firstIndex >= 0);
1865 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001866 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001867 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001868 if (!sortable) {
1869 unsortable = true;
1870 return NULL;
1871 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001872 SkASSERT(sorted[firstIndex]->segment() == this);
1873 #if DEBUG_WINDING
1874 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
1875 #endif
1876 int aSumWinding = winding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001877 int bSumWinding = oppWinding;
1878 bool angleIsOp = sorted[firstIndex]->segment()->operand() ^ operand();
caryclark@google.com235f56a2012-09-14 14:19:30 +00001879 int angleSpan = spanSign(sorted[firstIndex]);
1880 if (angleIsOp) {
1881 bSumWinding -= angleSpan;
1882 } else {
1883 aSumWinding -= angleSpan;
1884 }
1885 int nextIndex = firstIndex + 1;
1886 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1887 const Angle* foundAngle = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001888 bool foundDone = false;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001889#define TWO_CHANNEL_DONE 0
1890#if TWO_CHANNEL_DONE
caryclark@google.com235f56a2012-09-14 14:19:30 +00001891 bool foundDone2 = false;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001892#define FOUND_DONE2 foundDone2
1893#else
1894#define FOUND_DONE2 foundDone
1895#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001896 // iterate through the angle, and compute everyone's winding
caryclark@google.com31143cf2012-11-09 22:14:19 +00001897 bool aAltFlipped = false;
1898 bool bAltFlipped = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001899 bool foundFlipped = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001900 int foundSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001901 int foundOppWinding = SK_MinS32;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001902 Segment* nextSegment;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001903 int aLastNonZeroSum = winding;
1904 int bLastNonZeroSum = oppWinding;
1905 bool foundOpp;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001906 do {
1907 if (nextIndex == angleCount) {
1908 nextIndex = 0;
1909 }
1910 const Angle* nextAngle = sorted[nextIndex];
1911 nextSegment = nextAngle->segment();
caryclark@google.comf839c032012-10-26 21:03:50 +00001912 bool nextDone = nextSegment->done(*nextAngle);
1913 bool nextTiny = nextSegment->tiny(*nextAngle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001914 angleIsOp = nextSegment->operand() ^ operand();
1915 int deltaSum = nextSegment->spanSign(nextAngle);
1916 int maxWinding, xorMask, sumWinding;
1917 bool otherNonZero, altFlipped;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001918 if (angleIsOp) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001919 maxWinding = bSumWinding;
1920 if (bSumWinding) {
1921 bLastNonZeroSum = bSumWinding;
1922 }
1923 bSumWinding -= deltaSum;
1924 sumWinding = bSumWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001925 otherNonZero = aSumWinding & aXorMask;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001926 xorMask = bXorMask;
1927 bAltFlipped ^= bLastNonZeroSum * bSumWinding < 0; // flip if different signs
1928 altFlipped = bAltFlipped;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001929 } else {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001930 maxWinding = aSumWinding;
1931 if (aSumWinding) {
1932 aLastNonZeroSum = aSumWinding;
1933 }
1934 aSumWinding -= deltaSum;
1935 sumWinding = aSumWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001936 otherNonZero = bSumWinding & bXorMask;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001937 xorMask = aXorMask;
1938 aAltFlipped ^= aLastNonZeroSum * aSumWinding < 0; // flip if different signs
1939 altFlipped = aAltFlipped;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001940 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00001941 bool opIsActive = activeOp(nextSegment->operand(), otherNonZero, op);
1942 int oWinding = angleIsOp ? aSumWinding : bSumWinding;
1943 if (!(sumWinding & xorMask)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001944 if (!active) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001945 markAndChaseDone(startIndex, endIndex, outerWinding, oppWinding);
1946 nextSegment->markAndChaseWinding(nextAngle, maxWinding, oWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001947 #if DEBUG_WINDING
1948 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
1949 #endif
1950 return NULL;
1951 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00001952 if (opIsActive && (!foundAngle || foundDone)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001953 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00001954 foundDone = nextDone && !nextTiny;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001955 foundFlipped = altFlipped;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001956 foundSum = 0;
1957 foundOpp = angleIsOp;
1958 foundOppWinding = oWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001959 }
1960 continue;
1961 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00001962 if (opIsActive && !(maxWinding & xorMask) && (!foundAngle || FOUND_DONE2)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001963 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00001964 if (foundAngle && FOUND_DONE2) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001965 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
1966 }
1967 #endif
1968 foundAngle = nextAngle;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001969 FOUND_DONE2 = nextDone && !nextTiny;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001970 foundFlipped = altFlipped;
1971 foundSum = sumWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001972 foundOpp = angleIsOp;
1973 foundOppWinding = oWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001974 }
1975 if (nextSegment->done()) {
1976 continue;
1977 }
1978 // if the winding is non-zero, nextAngle does not connect to
1979 // current chain. If we haven't done so already, mark the angle
1980 // as done, record the winding value, and mark connected unambiguous
1981 // segments as well.
1982 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
1983 if (useInnerWinding(maxWinding, sumWinding)) {
1984 maxWinding = sumWinding;
1985 }
1986 Span* last;
1987 if (foundAngle) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001988 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding, oWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001989 } else {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001990 last = nextSegment->markAndChaseDone(nextAngle, maxWinding, oWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001991 }
1992 if (last) {
1993 *chase.append() = last;
1994 }
1995 }
1996 } while (++nextIndex != lastIndex);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001997 markDone(SkMin32(startIndex, endIndex), outerWinding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001998 if (!foundAngle) {
1999 return NULL;
2000 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002001 #if DEBUG_WINDING
2002 int oldSpanSign = spanSign(nextStart, nextEnd);
2003 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002004 nextStart = foundAngle->start();
2005 nextEnd = foundAngle->end();
2006 nextSegment = foundAngle->segment();
2007 int flipped = foundFlipped ? -1 : 1;
2008 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2009 SkMin32(nextStart, nextEnd));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002010 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00002011 SkDebugf("%s foundFlipped=%d spanWinding=%d oldSpanSign=%d spanSign=%d\n",
2012 __FUNCTION__, foundFlipped, spanWinding, oldSpanSign,
2013 nextSegment->spanSign(foundAngle));
2014 SkDebugf("%s foundOpp=%d oppWinding=%d foundOppWinding=%d winding=%d foundSum=",
2015 __FUNCTION__, foundOpp, oppWinding, foundOppWinding, winding);
2016 if (foundSum == SK_MinS32) {
2017 SkDebugf("?");
2018 } else {
2019 SkDebugf("%d", foundSum);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002020 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002021 SkDebugf("\n");
2022 #endif
2023 if (oppWinding != foundOppWinding) {
2024 oppWinding = foundOppWinding;
2025 if (foundOpp) {
2026 SkASSERT(foundSum != SK_MinS32);
2027 winding = foundSum;
2028 spanWinding = nextSegment->spanSign(foundAngle);
2029 }
2030 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002031 return nextSegment;
2032 }
caryclark@google.com47580692012-07-23 12:14:49 +00002033
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002034 // so the span needs to contain the pairing info found here
2035 // this should include the winding computed for the edge, and
2036 // what edge it connects to, and whether it is discarded
2037 // (maybe discarded == abs(winding) > 1) ?
2038 // only need derivatives for duration of sorting, add a new struct
2039 // for pairings, remove extra spans that have zero length and
2040 // reference an unused other
2041 // for coincident, the last span on the other may be marked done
2042 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002043
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002044 // if loop is exhausted, contour may be closed.
2045 // FIXME: pass in close point so we can check for closure
2046
2047 // given a segment, and a sense of where 'inside' is, return the next
2048 // segment. If this segment has an intersection, or ends in multiple
2049 // segments, find the mate that continues the outside.
2050 // note that if there are multiples, but no coincidence, we can limit
2051 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002052
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002053 // mark found segments as done
2054
caryclark@google.com15fa1382012-05-07 20:49:36 +00002055 // start is the index of the beginning T of this edge
2056 // it is guaranteed to have an end which describes a non-zero length (?)
2057 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002058 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002059 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2060 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002061 const int startIndex = nextStart;
2062 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002063 int outerWinding = winding;
2064 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002065 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002066 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2067 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002068 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002069 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002070 outerWinding = innerWinding;
2071 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002072 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002073 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002074 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2075 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002076 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002077 int end = nextExactSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002078 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002079 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002080 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002081 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002082 // mark the smaller of startIndex, endIndex done, and all adjacent
2083 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002084 #if DEBUG_WINDING
2085 SkDebugf("%s simple\n", __FUNCTION__);
2086 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002087 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002088 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002089 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002090 double startT = other->fTs[nextStart].fT;
2091 nextEnd = nextStart;
2092 do {
2093 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002094 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002095 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00002096 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002097 return other;
2098 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002099 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002100 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002101 SkASSERT(startIndex - endIndex != 0);
2102 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002103 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002104 buildAngles(end, angles, false);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002105 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002106 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002107 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002108 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002109 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002110 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002111 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002112 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002113 if (!sortable) {
2114 unsortable = true;
2115 return NULL;
2116 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002117 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002118 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002119 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002120 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002121 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002122 int nextIndex = firstIndex + 1;
2123 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2124 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002125 // FIXME: found done logic probably fails if there are more than 4
2126 // sorted angles. It should bias towards the first and last undone
2127 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002128 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002129 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002130 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002131 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002132 bool altFlipped = false;
2133 bool foundFlipped = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002134 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002135 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002136 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002137 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002138 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002139 nextIndex = 0;
2140 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002141 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002142 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002143 if (sumWinding) {
2144 lastNonZeroSum = sumWinding;
2145 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002146 nextSegment = nextAngle->segment();
caryclark@google.comf839c032012-10-26 21:03:50 +00002147 bool nextDone = nextSegment->done(*nextAngle);
2148 bool nextTiny = nextSegment->tiny(*nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002149 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002150 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002151 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002152 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002153 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2154 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002155 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002156 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002157 if (!active) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002158 // FIXME: shouldn't this call mark and chase done ?
caryclark@google.com59823f72012-08-09 18:17:47 +00002159 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002160 // FIXME: shouldn't this call mark and chase winding ?
caryclark@google.com47580692012-07-23 12:14:49 +00002161 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002162 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002163 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002164 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002165 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002166 return NULL;
2167 }
caryclark@google.com47580692012-07-23 12:14:49 +00002168 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002169 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002170 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002171 foundFlipped = altFlipped;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002172 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002173 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002174 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002175
caryclark@google.com24bec792012-08-20 12:43:57 +00002176 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002177 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002178 if (foundAngle && foundDone2) {
2179 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002180 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002181 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002182 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002183 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002184 foundFlipped = altFlipped;
2185 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002186 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002187 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002188 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002189 }
2190 // if the winding is non-zero, nextAngle does not connect to
2191 // current chain. If we haven't done so already, mark the angle
2192 // as done, record the winding value, and mark connected unambiguous
2193 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002194 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002195 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002196 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002197 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002198 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002199 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002200 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002201 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002202 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002203 }
2204 if (last) {
2205 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002206 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002207 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002208 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002209 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002210 if (!foundAngle) {
2211 return NULL;
2212 }
2213 nextStart = foundAngle->start();
2214 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002215 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002216 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002217 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2218 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002219 if (winding) {
2220 #if DEBUG_WINDING
2221 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2222 if (foundSum == SK_MinS32) {
2223 SkDebugf("?");
2224 } else {
2225 SkDebugf("%d", foundSum);
2226 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002227 SkDebugf("\n");
2228 #endif
2229 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002230 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002231 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002232 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002233 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002234 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002235 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002236
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002237 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002238 const int startIndex = nextStart;
2239 const int endIndex = nextEnd;
2240 SkASSERT(startIndex != endIndex);
2241 int count = fTs.count();
2242 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2243 : startIndex > 0);
2244 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002245 int end = nextExactSpan(startIndex, step);
caryclark@google.com24bec792012-08-20 12:43:57 +00002246 SkASSERT(end >= 0);
2247 Span* endSpan = &fTs[end];
2248 Segment* other;
2249 markDone(SkMin32(startIndex, endIndex), 1);
2250 if (isSimple(end)) {
2251 #if DEBUG_WINDING
2252 SkDebugf("%s simple\n", __FUNCTION__);
2253 #endif
2254 other = endSpan->fOther;
2255 nextStart = endSpan->fOtherIndex;
2256 double startT = other->fTs[nextStart].fT;
2257 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002258 if ((approximately_less_than_zero(startT) && step < 0)
2259 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002260 step = -step;
2261 SkDEBUGCODE(firstLoop = false;)
2262 }
2263 do {
2264 nextEnd = nextStart;
2265 do {
2266 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002267 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002268 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002269 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2270 break;
2271 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002272 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002273 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002274 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002275 SkDEBUGCODE(firstLoop = false;)
2276 step = -step;
2277 } while (true);
2278 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2279 return other;
2280 }
2281 SkTDArray<Angle> angles;
2282 SkASSERT(startIndex - endIndex != 0);
2283 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2284 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002285 buildAngles(end, angles, false);
caryclark@google.com24bec792012-08-20 12:43:57 +00002286 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002287 bool sortable = SortAngles(angles, sorted);
caryclark@google.com24bec792012-08-20 12:43:57 +00002288 int angleCount = angles.count();
2289 int firstIndex = findStartingEdge(sorted, startIndex, end);
2290 SkASSERT(firstIndex >= 0);
2291 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002292 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002293 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002294 if (!sortable) {
2295 unsortable = true;
2296 return NULL;
2297 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002298 SkASSERT(sorted[firstIndex]->segment() == this);
2299 int nextIndex = firstIndex + 1;
2300 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2301 const Angle* nextAngle;
2302 Segment* nextSegment;
2303 do {
2304 if (nextIndex == angleCount) {
2305 nextIndex = 0;
2306 }
2307 nextAngle = sorted[nextIndex];
2308 nextSegment = nextAngle->segment();
2309 if (!nextSegment->done(*nextAngle)) {
2310 break;
2311 }
2312 if (++nextIndex == lastIndex) {
2313 return NULL;
2314 }
2315 } while (true);
2316 nextStart = nextAngle->start();
2317 nextEnd = nextAngle->end();
2318 return nextSegment;
2319 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002320
2321 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2322 int angleCount = sorted.count();
2323 int firstIndex = -1;
2324 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2325 const Angle* angle = sorted[angleIndex];
2326 if (angle->segment() == this && angle->start() == end &&
2327 angle->end() == start) {
2328 firstIndex = angleIndex;
2329 break;
2330 }
2331 }
2332 return firstIndex;
2333 }
2334
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002335 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com235f56a2012-09-14 14:19:30 +00002336 void findTooCloseToCall(bool isXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002337 int count = fTs.count();
2338 if (count < 3) { // require t=0, x, 1 at minimum
2339 return;
2340 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002341 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002342 int moCount;
2343 Span* match;
2344 Segment* mOther;
2345 do {
2346 match = &fTs[matchIndex];
2347 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002348 // FIXME: allow quads, cubics to be near coincident?
2349 if (mOther->fVerb == SkPath::kLine_Verb) {
2350 moCount = mOther->fTs.count();
2351 if (moCount >= 3) {
2352 break;
2353 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002354 }
2355 if (++matchIndex >= count) {
2356 return;
2357 }
2358 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002359 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002360 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002361 // look for a pair of nearby T values that map to the same (x,y) value
2362 // if found, see if the pair of other segments share a common point. If
2363 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002364 for (int index = matchIndex + 1; index < count; ++index) {
2365 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002366 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002367 continue;
2368 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002369 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002370 if (tOther->fVerb != SkPath::kLine_Verb) {
2371 continue; // FIXME: allow quads, cubics to be near coincident?
2372 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002373 int toCount = tOther->fTs.count();
2374 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002375 continue;
2376 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002377 const SkPoint* testPt = &xyAtT(test);
2378 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002379 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002380 moCount = toCount;
2381 match = test;
2382 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002383 matchPt = testPt;
2384 continue;
2385 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002386 int moStart = -1;
2387 int moEnd = -1;
2388 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002389 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002390 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002391 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002392 continue;
2393 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002394 if (moSpan.fOther == this) {
2395 if (moSpan.fOtherT == match->fT) {
2396 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002397 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002398 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002399 continue;
2400 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002401 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002402 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2403 moStart = -1;
2404 break;
2405 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002406 SkASSERT(moEnd == -1);
2407 moEnd = moIndex;
2408 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002409 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002410 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002411 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002412 continue;
2413 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002414 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002415 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002416 continue;
2417 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002418 int toStart = -1;
2419 int toEnd = -1;
2420 double toStartT, toEndT;
2421 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2422 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002423 if (toSpan.fDone) {
2424 continue;
2425 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002426 if (toSpan.fOther == this) {
2427 if (toSpan.fOtherT == test->fT) {
2428 toStart = toIndex;
2429 toStartT = toSpan.fT;
2430 }
2431 continue;
2432 }
2433 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002434 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2435 moStart = -1;
2436 break;
2437 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002438 SkASSERT(toEnd == -1);
2439 toEnd = toIndex;
2440 toEndT = toSpan.fT;
2441 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002442 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002443 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2444 if (toStart <= 0 || toEnd <= 0) {
2445 continue;
2446 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002447 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002448 continue;
2449 }
2450 // test to see if the segment between there and here is linear
2451 if (!mOther->isLinear(moStart, moEnd)
2452 || !tOther->isLinear(toStart, toEnd)) {
2453 continue;
2454 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002455 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002456 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002457 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002458 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002459 // FIXME: this is bogus for multiple ops
2460 // the xorMask needs to be accumulated from the union of the two
2461 // edges -- which means that the segment must have its own copy of the mask
2462 mOther->addTCoincident(isXor, moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002463 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002464 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002465 }
2466
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002467 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002468 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002469 // a) mark spans with either end unsortable as done, or
2470 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2471 // when encountering an unsortable span
2472
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002473 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2474 // and use more concise logic like the old edge walker code?
2475 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002476 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002477 // iterate through T intersections and return topmost
2478 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002479 SkASSERT(!done());
2480 int firstT;
2481 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002482 SkPoint topPt;
2483 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002484 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002485 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002486 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002487 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002488 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002489 const Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00002490 if (span.fUnsortableStart | lastUnsortable) {
2491 goto next;
2492 }
2493 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002494 const SkPoint& intercept = xyAtT(&span);
2495 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2496 && topPt.fX > intercept.fX)) {
2497 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002498 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002499 } else if (topPt == intercept) {
2500 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002501 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002502 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002503 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002504 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002505 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002506 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002507 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002508 int step = 1;
2509 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002510 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002511 step = -1;
2512 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002513 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002514 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002515 // if the topmost T is not on end, or is three-way or more, find left
2516 // look for left-ness from tLeft to firstT (matching y of other)
2517 SkTDArray<Angle> angles;
2518 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002519 addTwoAngles(end, firstT, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002520 buildAngles(firstT, angles, true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002521 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002522 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002523 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002524 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002525 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00002526 if (!sortable) {
2527 return NULL;
2528 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002529 // skip edges that have already been processed
2530 firstT = -1;
2531 Segment* leftSegment;
2532 do {
2533 const Angle* angle = sorted[++firstT];
caryclark@google.comf839c032012-10-26 21:03:50 +00002534 SkASSERT(!angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002535 leftSegment = angle->segment();
2536 tIndex = angle->end();
2537 endIndex = angle->start();
2538 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002539 return leftSegment;
2540 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002541
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002542 // FIXME: not crazy about this
2543 // when the intersections are performed, the other index is into an
2544 // incomplete array. as the array grows, the indices become incorrect
2545 // while the following fixes the indices up again, it isn't smart about
2546 // skipping segments whose indices are already correct
2547 // assuming we leave the code that wrote the index in the first place
2548 void fixOtherTIndex() {
2549 int iCount = fTs.count();
2550 for (int i = 0; i < iCount; ++i) {
2551 Span& iSpan = fTs[i];
2552 double oT = iSpan.fOtherT;
2553 Segment* other = iSpan.fOther;
2554 int oCount = other->fTs.count();
2555 for (int o = 0; o < oCount; ++o) {
2556 Span& oSpan = other->fTs[o];
2557 if (oT == oSpan.fT && this == oSpan.fOther) {
2558 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002559 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002560 }
2561 }
2562 }
2563 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002564
caryclark@google.com495f8e42012-05-31 13:13:11 +00002565 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002566 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002567 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002568 SkASSERT(end >= 0);
2569 if (multipleSpans(end)) {
2570 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002571 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002572 const Span& endSpan = fTs[end];
2573 Segment* other = endSpan.fOther;
2574 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002575 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002576 Span* last = other->innerChaseDone(index, step, winding);
2577 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002578 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002579 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002580
caryclark@google.com31143cf2012-11-09 22:14:19 +00002581 Span* innerChaseDone(int index, int step, int winding, int oppWinding) {
2582 int end = nextExactSpan(index, step);
2583 SkASSERT(end >= 0);
2584 if (multipleSpans(end)) {
2585 return &fTs[end];
2586 }
2587 const Span& endSpan = fTs[end];
2588 Segment* other = endSpan.fOther;
2589 index = endSpan.fOtherIndex;
2590 int otherEnd = other->nextExactSpan(index, step);
2591 Span* last = other->innerChaseDone(index, step, winding, oppWinding);
2592 other->markDone(SkMin32(index, otherEnd), winding, oppWinding);
2593 return last;
2594 }
2595
2596
caryclark@google.com59823f72012-08-09 18:17:47 +00002597 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002598 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002599 SkASSERT(end >= 0);
2600 if (multipleSpans(end)) {
2601 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002602 }
2603 const Span& endSpan = fTs[end];
2604 Segment* other = endSpan.fOther;
2605 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002606 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002607 int min = SkMin32(index, otherEnd);
2608 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002609 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002610 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002611 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002612 Span* last = other->innerChaseWinding(index, step, winding);
2613 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002614 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002615 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002616
caryclark@google.com31143cf2012-11-09 22:14:19 +00002617 Span* innerChaseWinding(int index, int step, int winding, int oppWinding) {
2618 int end = nextExactSpan(index, step);
2619 SkASSERT(end >= 0);
2620 if (multipleSpans(end)) {
2621 return &fTs[end];
2622 }
2623 const Span& endSpan = fTs[end];
2624 Segment* other = endSpan.fOther;
2625 index = endSpan.fOtherIndex;
2626 int otherEnd = other->nextExactSpan(index, step);
2627 int min = SkMin32(index, otherEnd);
2628 if (other->fTs[min].fWindSum != SK_MinS32) {
2629 SkASSERT(other->fTs[min].fWindSum == winding);
2630 return NULL;
2631 }
2632 Span* last = other->innerChaseWinding(index, step, winding, oppWinding);
2633 other->markWinding(min, winding, oppWinding);
2634 return last;
2635 }
2636
caryclark@google.com235f56a2012-09-14 14:19:30 +00002637 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2638 fDoneSpans = 0;
2639 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002640 fPts = pts;
2641 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002642 }
2643
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002644 bool intersected() const {
2645 return fTs.count() > 0;
2646 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002647
2648 bool isConnected(int startIndex, int endIndex) const {
2649 return fTs[startIndex].fWindSum != SK_MinS32
2650 || fTs[endIndex].fWindSum != SK_MinS32;
2651 }
2652
caryclark@google.com235f56a2012-09-14 14:19:30 +00002653 bool isHorizontal() const {
2654 return fBounds.fTop == fBounds.fBottom;
2655 }
2656
caryclark@google.com15fa1382012-05-07 20:49:36 +00002657 bool isLinear(int start, int end) const {
2658 if (fVerb == SkPath::kLine_Verb) {
2659 return true;
2660 }
2661 if (fVerb == SkPath::kQuad_Verb) {
2662 SkPoint qPart[3];
2663 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2664 return QuadIsLinear(qPart);
2665 } else {
2666 SkASSERT(fVerb == SkPath::kCubic_Verb);
2667 SkPoint cPart[4];
2668 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2669 return CubicIsLinear(cPart);
2670 }
2671 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002672
2673 // OPTIMIZE: successive calls could start were the last leaves off
2674 // or calls could specialize to walk forwards or backwards
2675 bool isMissing(double startT) const {
2676 size_t tCount = fTs.count();
2677 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002678 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002679 return false;
2680 }
2681 }
2682 return true;
2683 }
2684
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002685 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002686 int count = fTs.count();
2687 if (count == 2) {
2688 return true;
2689 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002690 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002691 if (approximately_less_than_zero(t)) {
2692 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002693 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002694 if (approximately_greater_than_one(t)) {
2695 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002696 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002697 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002698 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002699
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002700 bool isVertical() const {
2701 return fBounds.fLeft == fBounds.fRight;
2702 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002703
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002704 SkScalar leftMost(int start, int end) const {
2705 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2706 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002707
caryclark@google.com495f8e42012-05-31 13:13:11 +00002708 // this span is excluded by the winding rule -- chase the ends
2709 // as long as they are unambiguous to mark connections as done
2710 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002711 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002712 int index = angle->start();
2713 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002714 return markAndChaseDone(index, endIndex, winding);
2715 }
2716
2717 Span* markAndChaseDone(const Angle* angle, int winding, int oppWinding) {
2718 int index = angle->start();
2719 int endIndex = angle->end();
2720 return markAndChaseDone(index, endIndex, winding, oppWinding);
2721 }
2722
2723 Span* markAndChaseDone(int index, int endIndex, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002724 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002725 Span* last = innerChaseDone(index, step, winding);
2726 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002727 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002728 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002729
caryclark@google.com31143cf2012-11-09 22:14:19 +00002730 Span* markAndChaseDone(int index, int endIndex, int winding, int oppWinding) {
2731 int step = SkSign32(endIndex - index);
2732 Span* last = innerChaseDone(index, step, winding, oppWinding);
2733 markDone(SkMin32(index, endIndex), winding, oppWinding);
2734 return last;
2735 }
2736
caryclark@google.com59823f72012-08-09 18:17:47 +00002737 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002738 int index = angle->start();
2739 int endIndex = angle->end();
2740 int min = SkMin32(index, endIndex);
2741 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002742 Span* last = innerChaseWinding(index, step, winding);
2743 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002744 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002745 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002746
caryclark@google.com31143cf2012-11-09 22:14:19 +00002747 Span* markAndChaseWinding(const Angle* angle, int winding, int oppWinding) {
2748 int index = angle->start();
2749 int endIndex = angle->end();
2750 int min = SkMin32(index, endIndex);
2751 int step = SkSign32(endIndex - index);
2752 Span* last = innerChaseWinding(index, step, winding, oppWinding);
2753 markWinding(min, winding, oppWinding);
2754 return last;
2755 }
2756
caryclark@google.com495f8e42012-05-31 13:13:11 +00002757 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002758 // This may be called when the segment is already marked done. While this
2759 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002760 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002761 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002762 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002763 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002764 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002765 double referenceT = fTs[index].fT;
2766 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002767 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2768 markOneDone(__FUNCTION__, lesser, winding);
2769 }
2770 do {
2771 markOneDone(__FUNCTION__, index, winding);
2772 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002773 }
2774
2775 void markDone(int index, int winding, int oppWinding) {
2776 // SkASSERT(!done());
2777 SkASSERT(winding);
2778 double referenceT = fTs[index].fT;
2779 int lesser = index;
2780 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2781 markOneDone(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002782 }
2783 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002784 markOneDone(__FUNCTION__, index, winding, oppWinding);
2785 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002786 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002787
caryclark@google.com24bec792012-08-20 12:43:57 +00002788 void markOneDone(const char* funName, int tIndex, int winding) {
2789 Span* span = markOneWinding(funName, tIndex, winding);
2790 if (!span) {
2791 return;
2792 }
2793 span->fDone = true;
2794 fDoneSpans++;
2795 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002796
caryclark@google.com31143cf2012-11-09 22:14:19 +00002797 void markOneDone(const char* funName, int tIndex, int winding, int oppWinding) {
2798 Span* span = markOneWinding(funName, tIndex, winding, oppWinding);
2799 if (!span) {
2800 return;
2801 }
2802 span->fDone = true;
2803 fDoneSpans++;
2804 }
2805
caryclark@google.com24bec792012-08-20 12:43:57 +00002806 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2807 Span& span = fTs[tIndex];
2808 if (span.fDone) {
2809 return NULL;
2810 }
2811 #if DEBUG_MARK_DONE
2812 debugShowNewWinding(funName, span, winding);
2813 #endif
2814 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002815 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002816 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002817 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002818 span.fWindSum = winding;
2819 return &span;
2820 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002821
caryclark@google.com31143cf2012-11-09 22:14:19 +00002822 Span* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding) {
2823 Span& span = fTs[tIndex];
2824 if (span.fDone) {
2825 return NULL;
2826 }
2827 #if DEBUG_MARK_DONE
2828 debugShowNewWinding(funName, span, winding, oppWinding);
2829 #endif
2830 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
2831 #ifdef SK_DEBUG
2832 SkASSERT(abs(winding) <= gDebugMaxWindSum);
2833 #endif
2834 span.fWindSum = winding;
2835 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
2836 #ifdef SK_DEBUG
2837 SkASSERT(abs(oppWinding) <= gDebugMaxWindSum);
2838 #endif
2839 span.fOppSum = oppWinding;
2840 return &span;
2841 }
2842
caryclark@google.comf839c032012-10-26 21:03:50 +00002843 // note that just because a span has one end that is unsortable, that's
2844 // not enough to mark it done. The other end may be sortable, allowing the
2845 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002846 void markUnsortable(int start, int end) {
2847 Span* span = &fTs[start];
2848 if (start < end) {
2849 span->fUnsortableStart = true;
2850 } else {
2851 --span;
2852 span->fUnsortableEnd = true;
2853 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002854 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002855 return;
2856 }
2857 span->fDone = true;
2858 fDoneSpans++;
2859 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002860
caryclark@google.com59823f72012-08-09 18:17:47 +00002861 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002862 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002863 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002864 double referenceT = fTs[index].fT;
2865 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002866 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2867 markOneWinding(__FUNCTION__, lesser, winding);
2868 }
2869 do {
2870 markOneWinding(__FUNCTION__, index, winding);
2871 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002872 }
2873
2874 void markWinding(int index, int winding, int oppWinding) {
2875 // SkASSERT(!done());
2876 SkASSERT(winding);
2877 double referenceT = fTs[index].fT;
2878 int lesser = index;
2879 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2880 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002881 }
2882 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002883 markOneWinding(__FUNCTION__, index, winding, oppWinding);
2884 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002885 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002886
caryclark@google.com2ddff932012-08-07 21:25:27 +00002887 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002888 int nextDoorWind = SK_MaxS32;
2889 if (tIndex > 0) {
2890 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002891 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002892 nextDoorWind = below.fWindValue;
2893 }
2894 }
2895 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2896 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002897 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002898 nextDoorWind = above.fWindValue;
2899 }
2900 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002901 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2902 const Span& below = fTs[tIndex - 1];
2903 nextDoorWind = below.fWindValue;
2904 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002905 if (nextDoorWind != SK_MaxS32) {
2906 Span& newSpan = fTs[tIndex];
2907 newSpan.fWindValue = nextDoorWind;
caryclark@google.comf839c032012-10-26 21:03:50 +00002908 if (!nextDoorWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002909 newSpan.fDone = true;
2910 ++fDoneSpans;
2911 }
2912 }
2913 }
2914
caryclark@google.com9764cc62012-07-12 19:29:45 +00002915 // return span if when chasing, two or more radiating spans are not done
2916 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2917 // candidate and the remaining spans have windValue == 0 (canceled by
2918 // coincidence). The coincident edges could either be removed altogether,
2919 // or this code could be more complicated in detecting this case. Worth it?
2920 bool multipleSpans(int end) const {
2921 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002922 }
2923
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002924 // This has callers for two different situations: one establishes the end
2925 // of the current span, and one establishes the beginning of the next span
2926 // (thus the name). When this is looking for the end of the current span,
2927 // coincidence is found when the beginning Ts contain -step and the end
2928 // contains step. When it is looking for the beginning of the next, the
2929 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002930 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002931 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002932 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002933 int count = fTs.count();
2934 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002935 while (step > 0 ? ++to < count : --to >= 0) {
2936 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002937 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002938 continue;
2939 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002940 return to;
2941 }
2942 return -1;
2943 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00002944
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002945 // FIXME
2946 // this returns at any difference in T, vs. a preset minimum. It may be
2947 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002948 // OPTIMIZATION splitting this into separate loops for up/down steps
2949 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002950 int nextExactSpan(int from, int step) const {
2951 const Span& fromSpan = fTs[from];
2952 int count = fTs.count();
2953 int to = from;
2954 while (step > 0 ? ++to < count : --to >= 0) {
2955 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00002956 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002957 continue;
2958 }
2959 return to;
2960 }
2961 return -1;
2962 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002963
caryclark@google.com235f56a2012-09-14 14:19:30 +00002964 bool operand() const {
2965 return fOperand;
2966 }
2967
caryclark@google.com31143cf2012-11-09 22:14:19 +00002968 int oppSum(int tIndex) const {
2969 return fTs[tIndex].fOppSum;
2970 }
2971
2972 int oppSum(const Angle* angle) const {
2973 int lesser = SkMin32(angle->start(), angle->end());
2974 return fTs[lesser].fOppSum;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002975 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002976
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002977 const SkPoint* pts() const {
2978 return fPts;
2979 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002980
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002981 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002982 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002983 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2984 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002985 }
2986
caryclark@google.comf839c032012-10-26 21:03:50 +00002987 // This marks all spans unsortable so that this info is available for early
2988 // exclusion in find top and others. This could be optimized to only mark
2989 // adjacent spans that unsortable. However, this makes it difficult to later
2990 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002991 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00002992 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002993 int angleCount = angles.count();
2994 int angleIndex;
2995 angleList.setReserve(angleCount);
2996 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002997 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00002998 *angleList.append() = &angle;
2999 sortable &= !angle.unsortable();
3000 }
3001 if (sortable) {
3002 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3003 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3004 if (angles[angleIndex].unsortable()) {
3005 sortable = false;
3006 break;
3007 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003008 }
3009 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003010 if (!sortable) {
3011 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3012 Angle& angle = angles[angleIndex];
3013 angle.segment()->markUnsortable(angle.start(), angle.end());
3014 }
3015 }
3016 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003017 }
3018
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003019 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003020 const Span& span(int tIndex) const {
3021 return fTs[tIndex];
3022 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003023
caryclark@google.com235f56a2012-09-14 14:19:30 +00003024 int spanSign(const Angle* angle) const {
3025 SkASSERT(angle->segment() == this);
3026 return spanSign(angle->start(), angle->end());
3027 }
3028
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003029 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003030 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue
3031 : fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003032#if DEBUG_WIND_BUMP
3033 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3034#endif
3035 return result;
3036 }
3037
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003038 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003039 double t(int tIndex) const {
3040 return fTs[tIndex].fT;
3041 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003042
caryclark@google.comf839c032012-10-26 21:03:50 +00003043 bool tiny(const Angle& angle) const {
3044 int start = angle.start();
3045 int end = angle.end();
3046 const Span& mSpan = fTs[SkMin32(start, end)];
3047 return mSpan.fTiny;
3048 }
3049
caryclark@google.com18063442012-07-25 12:05:18 +00003050 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3051 double start) {
3052 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003053 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003054 *outsideTs.append() = end;
3055 *outsideTs.append() = start;
3056 }
3057 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003058
caryclark@google.com24bec792012-08-20 12:43:57 +00003059 void undoneSpan(int& start, int& end) {
3060 size_t tCount = fTs.count();
3061 size_t index;
3062 for (index = 0; index < tCount; ++index) {
3063 if (!fTs[index].fDone) {
3064 break;
3065 }
3066 }
3067 SkASSERT(index < tCount - 1);
3068 start = index;
3069 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003070 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003071 SkASSERT(index < tCount);
3072 SkASSERT(index < tCount);
3073 end = index;
3074 }
caryclark@google.com18063442012-07-25 12:05:18 +00003075
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003076 bool unsortable(int index) const {
3077 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3078 }
3079
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003080 void updatePts(const SkPoint pts[]) {
3081 fPts = pts;
3082 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003083
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003084 SkPath::Verb verb() const {
3085 return fVerb;
3086 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003087
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003088 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003089 return fTs[tIndex].fWindSum;
3090 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003091
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003092 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003093 int start = angle->start();
3094 int end = angle->end();
3095 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003096 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003097 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003098
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003099 int windValue(int tIndex) const {
3100 return fTs[tIndex].fWindValue;
3101 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003102
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003103 int windValue(const Angle* angle) const {
3104 int start = angle->start();
3105 int end = angle->end();
3106 int index = SkMin32(start, end);
3107 return windValue(index);
3108 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003109
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003110 SkScalar xAtT(const Span* span) const {
3111 return xyAtT(span).fX;
3112 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003113
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003114 const SkPoint& xyAtT(int index) const {
3115 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003116 }
3117
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003118 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003119 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003120 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003121 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003122 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003123 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003124 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003125 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003126 }
3127 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003128 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003129 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003130
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003131 SkScalar yAtT(int index) const {
3132 return yAtT(&fTs[index]);
3133 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003134
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003135 SkScalar yAtT(const Span* span) const {
3136 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003137 }
3138
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003139#if DEBUG_DUMP
3140 void dump() const {
3141 const char className[] = "Segment";
3142 const int tab = 4;
3143 for (int i = 0; i < fTs.count(); ++i) {
3144 SkPoint out;
3145 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3146 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003147 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003148 tab + sizeof(className), className, fID,
3149 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003150 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003151 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003152 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003153 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003154 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003155 }
3156#endif
3157
caryclark@google.com47580692012-07-23 12:14:49 +00003158#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003159 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003160 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003161 for (int i = 0; i < fTs.count(); ++i) {
3162 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3163 return;
3164 }
3165 }
3166 SkASSERT(0);
3167 }
3168#endif
3169
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003170#if DEBUG_DUMP
3171 int debugID() const {
3172 return fID;
3173 }
3174#endif
3175
caryclark@google.com24bec792012-08-20 12:43:57 +00003176#if DEBUG_WINDING
3177 void debugShowSums() const {
3178 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3179 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3180 for (int i = 0; i < fTs.count(); ++i) {
3181 const Span& span = fTs[i];
3182 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3183 if (span.fWindSum == SK_MinS32) {
3184 SkDebugf("?");
3185 } else {
3186 SkDebugf("%d", span.fWindSum);
3187 }
3188 SkDebugf("]");
3189 }
3190 SkDebugf("\n");
3191 }
3192#endif
3193
caryclark@google.comcc905052012-07-25 20:59:42 +00003194#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003195 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003196 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00003197 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00003198 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00003199 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
3200 }
3201 SkDebugf("\n");
3202 }
3203#endif
3204
caryclark@google.com027de222012-07-12 12:52:50 +00003205#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003206 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003207 if (done()) {
3208 return;
3209 }
3210 for (int i = 0; i < fTs.count(); ++i) {
3211 if (fTs[i].fDone) {
3212 continue;
3213 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003214 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003215 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3216 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3217 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3218 }
3219 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003220 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003221 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003222 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003223 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3224 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3225 if (fTs[i].fWindSum == SK_MinS32) {
3226 SkDebugf("?");
3227 } else {
3228 SkDebugf("%d", fTs[i].fWindSum);
3229 }
3230 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003231 }
3232 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003233
3234 // This isn't useful yet -- but leaving it in for now in case i think of something
3235 // to use it for
3236 void validateActiveSpans() const {
3237 if (done()) {
3238 return;
3239 }
3240 int tCount = fTs.count();
3241 for (int index = 0; index < tCount; ++index) {
3242 if (fTs[index].fDone) {
3243 continue;
3244 }
3245 // count number of connections which are not done
3246 int first = index;
3247 double baseT = fTs[index].fT;
3248 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3249 --first;
3250 }
3251 int last = index;
3252 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3253 ++last;
3254 }
3255 int connections = 0;
3256 connections += first > 0 && !fTs[first - 1].fDone;
3257 for (int test = first; test <= last; ++test) {
3258 connections += !fTs[test].fDone;
3259 const Segment* other = fTs[test].fOther;
3260 int oIndex = fTs[test].fOtherIndex;
3261 connections += !other->fTs[oIndex].fDone;
3262 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3263 }
3264 // SkASSERT(!(connections & 1));
3265 }
3266 }
caryclark@google.com027de222012-07-12 12:52:50 +00003267#endif
3268
caryclark@google.com0c803d02012-08-06 11:15:47 +00003269#if DEBUG_MARK_DONE
3270 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3271 const SkPoint& pt = xyAtT(&span);
3272 SkDebugf("%s id=%d", fun, fID);
3273 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3274 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3275 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3276 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003277 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3278 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3279 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3280 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003281 if (span.fWindSum == SK_MinS32) {
3282 SkDebugf("?");
3283 } else {
3284 SkDebugf("%d", span.fWindSum);
3285 }
3286 SkDebugf(" windValue=%d\n", span.fWindValue);
3287 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003288
3289 void debugShowNewWinding(const char* fun, const Span& span, int winding, int oppWinding) {
3290 const SkPoint& pt = xyAtT(&span);
3291 SkDebugf("%s id=%d", fun, fID);
3292 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3293 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3294 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3295 }
3296 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3297 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3298 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d newOppSum=%d oppSum=",
3299 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
3300 winding, oppWinding);
3301 if (span.fOppSum == SK_MinS32) {
3302 SkDebugf("?");
3303 } else {
3304 SkDebugf("%d", span.fOppSum);
3305 }
3306 SkDebugf(" windSum=");
3307 if (span.fWindSum == SK_MinS32) {
3308 SkDebugf("?");
3309 } else {
3310 SkDebugf("%d", span.fWindSum);
3311 }
3312 SkDebugf(" windValue=%d\n", span.fWindValue);
3313 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003314#endif
3315
caryclark@google.com47580692012-07-23 12:14:49 +00003316#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003317 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com31143cf2012-11-09 22:14:19 +00003318 const int contourWinding, const int oppContourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003319 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003320 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003321 int lastSum = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003322 int oppLastSum = oppContourWinding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003323 int windSum = lastSum - spanSign(angles[first]);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003324 int oppWindSum = oppLastSum;
3325 SkDebugf("%s %s contourWinding=%d oppContourWinding=%d sign=%d\n", fun, __FUNCTION__,
3326 contourWinding, oppContourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003327 int index = first;
3328 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003329 do {
3330 const Angle& angle = *angles[index];
3331 const Segment& segment = *angle.segment();
3332 int start = angle.start();
3333 int end = angle.end();
3334 const Span& sSpan = segment.fTs[start];
3335 const Span& eSpan = segment.fTs[end];
3336 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com31143cf2012-11-09 22:14:19 +00003337 bool opp = segment.fOperand ^ fOperand;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003338 if (!firstTime) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003339 if (opp) {
3340 oppLastSum = oppWindSum;
3341 oppWindSum -= segment.spanSign(&angle);
3342 } else {
3343 lastSum = windSum;
3344 windSum -= segment.spanSign(&angle);
3345 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00003346 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003347 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 +00003348 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003349 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003350 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003351 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3352 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3353 mSpan.fWindValue);
3354 if (mSpan.fWindSum == SK_MinS32) {
3355 SkDebugf("?");
3356 } else {
3357 SkDebugf("%d", mSpan.fWindSum);
3358 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003359 int last, wind;
3360 if (opp) {
3361 last = oppLastSum;
3362 wind = oppWindSum;
3363 } else {
3364 last = lastSum;
3365 wind = windSum;
3366 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003367 SkDebugf(" winding: %d->%d (max=%d) ", last, wind,
caryclark@google.com31143cf2012-11-09 22:14:19 +00003368 useInnerWinding(last, wind) ? wind : last);
3369 SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003370#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003371 angle.debugShow(segment.xyAtT(&sSpan));
3372#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003373 ++index;
3374 if (index == angles.count()) {
3375 index = 0;
3376 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003377 if (firstTime) {
3378 firstTime = false;
3379 }
caryclark@google.com47580692012-07-23 12:14:49 +00003380 } while (index != first);
3381 }
3382#endif
3383
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003384#if DEBUG_WINDING
3385 bool debugVerifyWinding(int start, int end, int winding) const {
3386 const Span& span = fTs[SkMin32(start, end)];
3387 int spanWinding = span.fWindSum;
3388 if (spanWinding == SK_MinS32) {
3389 return true;
3390 }
3391 int spanSign = SkSign32(start - end);
3392 int signedVal = spanSign * span.fWindValue;
3393 if (signedVal < 0) {
3394 spanWinding -= signedVal;
3395 }
3396 return span.fWindSum == winding;
3397 }
3398#endif
3399
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003400private:
3401 const SkPoint* fPts;
3402 SkPath::Verb fVerb;
3403 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003404 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00003405 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00003406 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003407#if DEBUG_DUMP
3408 int fID;
3409#endif
3410};
3411
caryclark@google.comb9738012012-07-03 19:53:30 +00003412class Contour;
3413
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003414struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003415 Contour* fContours[2];
3416 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003417 double fTs[2][2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00003418 bool fXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003419};
3420
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003421class Contour {
3422public:
3423 Contour() {
3424 reset();
3425#if DEBUG_DUMP
3426 fID = ++gContourID;
3427#endif
3428 }
3429
3430 bool operator<(const Contour& rh) const {
3431 return fBounds.fTop == rh.fBounds.fTop
3432 ? fBounds.fLeft < rh.fBounds.fLeft
3433 : fBounds.fTop < rh.fBounds.fTop;
3434 }
3435
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003436 void addCoincident(int index, Contour* other, int otherIndex,
3437 const Intersections& ts, bool swap) {
3438 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00003439 coincidence.fContours[0] = this;
3440 coincidence.fContours[1] = other;
3441 coincidence.fSegments[0] = index;
3442 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003443 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3444 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3445 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3446 coincidence.fTs[swap][0] = ts.fT[0][0];
3447 coincidence.fTs[swap][1] = ts.fT[0][1];
3448 coincidence.fTs[!swap][0] = ts.fT[1][0];
3449 coincidence.fTs[!swap][1] = ts.fT[1][1];
3450 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3451 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3452 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3453 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3454 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3455 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3456 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003457 coincidence.fXor = fOperand == other->fOperand ? fXor : true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003458 }
3459
3460 void addCross(const Contour* crosser) {
3461#ifdef DEBUG_CROSS
3462 for (int index = 0; index < fCrosses.count(); ++index) {
3463 SkASSERT(fCrosses[index] != crosser);
3464 }
3465#endif
3466 *fCrosses.append() = crosser;
3467 }
3468
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003469 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003470 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003471 fContainsCurves = true;
3472 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003473
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003474 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003475 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003476 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003477 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003478
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003479 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3480 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3481 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003482
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003483 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003484 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003485 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003486 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003487 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003488
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003489 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3490 containsIntercepts();
3491 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3492 }
3493
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003494 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003495 return fBounds;
3496 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003497
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003498 void complete() {
3499 setBounds();
3500 fContainsIntercepts = false;
3501 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003502
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003503 void containsIntercepts() {
3504 fContainsIntercepts = true;
3505 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003506
rmistry@google.comd6176b02012-08-23 18:14:13 +00003507 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003508 int &tIndex, double& hitT) {
3509 int segmentCount = fSegments.count();
3510 const Segment* bestSegment = NULL;
3511 for (int test = 0; test < segmentCount; ++test) {
3512 Segment* testSegment = &fSegments[test];
3513 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003514 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003515 continue;
3516 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003517 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003518 continue;
3519 }
3520 if (bounds.fLeft > basePt.fX) {
3521 continue;
3522 }
3523 if (bounds.fRight < basePt.fX) {
3524 continue;
3525 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003526 if (bounds.fLeft == bounds.fRight) {
3527 continue;
3528 }
3529 #if 0
3530 bool leftHalf = bounds.fLeft == basePt.fX;
3531 bool rightHalf = bounds.fRight == basePt.fX;
3532 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3533 basePt, leftHalf, rightHalf)) {
3534 continue;
3535 }
3536 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003537 double testHitT;
3538 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3539 if (testT >= 0) {
3540 bestSegment = testSegment;
3541 tIndex = testT;
3542 hitT = testHitT;
3543 }
3544 }
3545 return bestSegment;
3546 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003547
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003548 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003549 for (int index = 0; index < fCrosses.count(); ++index) {
3550 if (fCrosses[index] == crosser) {
3551 return true;
3552 }
3553 }
3554 return false;
3555 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003556
caryclark@google.comf839c032012-10-26 21:03:50 +00003557 const SkPoint& end() const {
3558 const Segment& segment = fSegments.back();
3559 return segment.pts()[segment.verb()];
3560 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003561
caryclark@google.com235f56a2012-09-14 14:19:30 +00003562 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003563 int segmentCount = fSegments.count();
3564 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003565 fSegments[sIndex].findTooCloseToCall(fXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003566 }
3567 }
3568
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003569 void fixOtherTIndex() {
3570 int segmentCount = fSegments.count();
3571 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3572 fSegments[sIndex].fixOtherTIndex();
3573 }
3574 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003575
caryclark@google.com31143cf2012-11-09 22:14:19 +00003576 bool operand() const {
3577 return fOperand;
3578 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003579
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003580 void reset() {
3581 fSegments.reset();
3582 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003583 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003584 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003585
caryclark@google.com235f56a2012-09-14 14:19:30 +00003586 // FIXME: for binary ops, need to keep both ops winding contributions separately
3587 // in edge array
3588 void resolveCoincidence() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003589 int count = fCoincidences.count();
3590 for (int index = 0; index < count; ++index) {
3591 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003592 Contour* thisContour = coincidence.fContours[0];
3593 Contour* otherContour = coincidence.fContours[1];
3594 int thisIndex = coincidence.fSegments[0];
3595 int otherIndex = coincidence.fSegments[1];
3596 Segment& thisOne = thisContour->fSegments[thisIndex];
3597 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003598 #if DEBUG_CONCIDENT
3599 thisOne.debugShowTs();
3600 other.debugShowTs();
3601 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003602 double startT = coincidence.fTs[0][0];
3603 double endT = coincidence.fTs[0][1];
caryclark@google.com32546db2012-08-31 20:55:07 +00003604 bool opposite = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003605 if (startT > endT) {
3606 SkTSwap<double>(startT, endT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003607 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003608 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003609 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003610 double oStartT = coincidence.fTs[1][0];
3611 double oEndT = coincidence.fTs[1][1];
3612 if (oStartT > oEndT) {
3613 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003614 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003615 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003616 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com32546db2012-08-31 20:55:07 +00003617 if (opposite) {
caryclark@google.comb9738012012-07-03 19:53:30 +00003618 // make sure startT and endT have t entries
caryclark@google.com32546db2012-08-31 20:55:07 +00003619 SkASSERT(opposite);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003620 if (startT > 0 || oEndT < 1
3621 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3622 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003623 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003624 if (oStartT > 0 || endT < 1
3625 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3626 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003627 }
3628 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003629 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003630 if (startT > 0 || oStartT > 0
3631 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003632 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003633 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003634 if (endT < 1 || oEndT < 1
3635 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003636 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003637 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003638 thisOne.addTCoincident(coincidence.fXor, startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003639 }
caryclark@google.com47580692012-07-23 12:14:49 +00003640 #if DEBUG_CONCIDENT
3641 thisOne.debugShowTs();
3642 other.debugShowTs();
3643 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003644 }
3645 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003646
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003647 const SkTArray<Segment>& segments() {
3648 return fSegments;
3649 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003650
caryclark@google.com235f56a2012-09-14 14:19:30 +00003651 void setOperand(bool isOp) {
3652 fOperand = isOp;
3653 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003654
caryclark@google.com235f56a2012-09-14 14:19:30 +00003655 void setXor(bool isXor) {
3656 fXor = isXor;
3657 }
3658
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003659 void sortSegments() {
3660 int segmentCount = fSegments.count();
3661 fSortedSegments.setReserve(segmentCount);
3662 for (int test = 0; test < segmentCount; ++test) {
3663 *fSortedSegments.append() = &fSegments[test];
3664 }
3665 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
3666 fFirstSorted = 0;
3667 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003668
caryclark@google.comf839c032012-10-26 21:03:50 +00003669 const SkPoint& start() const {
3670 return fSegments.front().pts()[0];
3671 }
3672
3673 void toPath(PathWrapper& path) const {
3674 int segmentCount = fSegments.count();
3675 const SkPoint& pt = fSegments.front().pts()[0];
3676 path.deferredMove(pt);
3677 for (int test = 0; test < segmentCount; ++test) {
3678 fSegments[test].addCurveTo(0, 1, path, true);
3679 }
3680 path.close();
3681 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003682
caryclark@google.comf839c032012-10-26 21:03:50 +00003683 void toPartialBackward(PathWrapper& path) const {
3684 int segmentCount = fSegments.count();
3685 for (int test = segmentCount - 1; test >= 0; --test) {
3686 fSegments[test].addCurveTo(1, 0, path, true);
3687 }
3688 }
3689
3690 void toPartialForward(PathWrapper& path) const {
3691 int segmentCount = fSegments.count();
3692 for (int test = 0; test < segmentCount; ++test) {
3693 fSegments[test].addCurveTo(0, 1, path, true);
3694 }
3695 }
3696
3697#if 0 // FIXME: obsolete, remove
caryclark@google.com15fa1382012-05-07 20:49:36 +00003698 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3699 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003700 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003701 // segments' top, and not a true sort, so it could be ameniable to regular
3702 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003703 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003704 int segmentCount = fSegments.count();
3705 SkASSERT(segmentCount > 0);
3706 int best = -1;
3707 Segment* bestSegment = NULL;
3708 while (++best < segmentCount) {
3709 Segment* testSegment = &fSegments[best];
3710 if (testSegment->done()) {
3711 continue;
3712 }
3713 bestSegment = testSegment;
3714 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003715 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003716 if (!bestSegment) {
3717 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003718 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003719 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003720 for (int test = best + 1; test < segmentCount; ++test) {
3721 Segment* testSegment = &fSegments[test];
3722 if (testSegment->done()) {
3723 continue;
3724 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003725 if (testSegment->bounds().fTop > bestTop) {
3726 continue;
3727 }
3728 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003729 if (bestTop > testTop) {
3730 bestTop = testTop;
3731 bestSegment = testSegment;
3732 }
3733 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003734 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003735 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003736 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003737#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003738
caryclark@google.comf839c032012-10-26 21:03:50 +00003739 Segment* topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003740 int segmentCount = fSortedSegments.count();
3741 SkASSERT(segmentCount > 0);
3742 Segment* bestSegment = NULL;
caryclark@google.comf839c032012-10-26 21:03:50 +00003743 int sortedIndex = fFirstSorted;
3744 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
3745 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003746 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003747 if (sortedIndex == fFirstSorted) {
3748 ++fFirstSorted;
3749 }
3750 continue;
3751 }
3752 SkPoint testXY;
3753 testSegment->activeLeftTop(testXY);
3754 if (testXY.fY < topLeft.fY) {
3755 continue;
3756 }
3757 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
3758 continue;
3759 }
3760 if (bestXY.fY < testXY.fY) {
3761 continue;
3762 }
3763 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003764 continue;
3765 }
3766 bestSegment = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00003767 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003768 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003769 return bestSegment;
3770 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003771
caryclark@google.com24bec792012-08-20 12:43:57 +00003772 Segment* undoneSegment(int& start, int& end) {
3773 int segmentCount = fSegments.count();
3774 for (int test = 0; test < segmentCount; ++test) {
3775 Segment* testSegment = &fSegments[test];
3776 if (testSegment->done()) {
3777 continue;
3778 }
3779 testSegment->undoneSpan(start, end);
3780 return testSegment;
3781 }
3782 return NULL;
3783 }
3784
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003785 int updateSegment(int index, const SkPoint* pts) {
3786 Segment& segment = fSegments[index];
3787 segment.updatePts(pts);
3788 return segment.verb() + 1;
3789 }
3790
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003791#if DEBUG_TEST
3792 SkTArray<Segment>& debugSegments() {
3793 return fSegments;
3794 }
3795#endif
3796
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003797#if DEBUG_DUMP
3798 void dump() {
3799 int i;
3800 const char className[] = "Contour";
3801 const int tab = 4;
3802 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
3803 for (i = 0; i < fSegments.count(); ++i) {
3804 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
3805 className, i);
3806 fSegments[i].dump();
3807 }
3808 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
3809 tab + sizeof(className), className,
3810 fBounds.fLeft, fBounds.fTop,
3811 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003812 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
3813 className, fContainsIntercepts);
3814 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
3815 className, fContainsCurves);
3816 }
3817#endif
3818
caryclark@google.com027de222012-07-12 12:52:50 +00003819#if DEBUG_ACTIVE_SPANS
3820 void debugShowActiveSpans() {
3821 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003822 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00003823 }
3824 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003825
3826 void validateActiveSpans() {
3827 for (int index = 0; index < fSegments.count(); ++index) {
3828 fSegments[index].validateActiveSpans();
3829 }
3830 }
caryclark@google.com027de222012-07-12 12:52:50 +00003831#endif
3832
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003833protected:
3834 void setBounds() {
3835 int count = fSegments.count();
3836 if (count == 0) {
3837 SkDebugf("%s empty contour\n", __FUNCTION__);
3838 SkASSERT(0);
3839 // FIXME: delete empty contour?
3840 return;
3841 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003842 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003843 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003844 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003845 }
3846 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003847
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003848private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003849 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003850 SkTDArray<Segment*> fSortedSegments;
3851 int fFirstSorted;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003852 SkTDArray<Coincidence> fCoincidences;
3853 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003854 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003855 bool fContainsIntercepts;
3856 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003857 bool fOperand; // true for the second argument to a binary operator
3858 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003859#if DEBUG_DUMP
3860 int fID;
3861#endif
3862};
3863
3864class EdgeBuilder {
3865public:
3866
caryclark@google.comf839c032012-10-26 21:03:50 +00003867EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
3868 : fPath(path.nativePath())
3869 , fContours(contours)
3870{
3871 init();
3872}
3873
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003874EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003875 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003876 , fContours(contours)
3877{
caryclark@google.comf839c032012-10-26 21:03:50 +00003878 init();
3879}
3880
3881void init() {
3882 fCurrentContour = NULL;
3883 fOperand = false;
3884 fXorMask = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003885#if DEBUG_DUMP
3886 gContourID = 0;
3887 gSegmentID = 0;
3888#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00003889 fSecondHalf = preFetch();
3890}
3891
3892void addOperand(const SkPath& path) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003893 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
3894 fPathVerbs.pop();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003895 fPath = &path;
caryclark@google.comf839c032012-10-26 21:03:50 +00003896 fXorMask = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003897 preFetch();
3898}
3899
3900void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003901 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003902 complete();
3903 if (fCurrentContour && !fCurrentContour->segments().count()) {
3904 fContours.pop_back();
3905 }
3906 // correct pointers in contours since fReducePts may have moved as it grew
3907 int cIndex = 0;
3908 int extraCount = fExtra.count();
3909 SkASSERT(extraCount == 0 || fExtra[0] == -1);
3910 int eIndex = 0;
3911 int rIndex = 0;
3912 while (++eIndex < extraCount) {
3913 int offset = fExtra[eIndex];
3914 if (offset < 0) {
3915 ++cIndex;
3916 continue;
3917 }
3918 fCurrentContour = &fContours[cIndex];
3919 rIndex += fCurrentContour->updateSegment(offset - 1,
3920 &fReducePts[rIndex]);
3921 }
3922 fExtra.reset(); // we're done with this
3923}
3924
3925ShapeOpMask xorMask() const {
3926 return fXorMask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003927}
3928
3929protected:
3930
3931void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003932 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003933 fCurrentContour->complete();
3934 fCurrentContour = NULL;
3935 }
3936}
3937
caryclark@google.com235f56a2012-09-14 14:19:30 +00003938// FIXME:remove once we can access path pts directly
3939int preFetch() {
3940 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003941 SkPoint pts[4];
3942 SkPath::Verb verb;
3943 do {
3944 verb = iter.next(pts);
3945 *fPathVerbs.append() = verb;
3946 if (verb == SkPath::kMove_Verb) {
3947 *fPathPts.append() = pts[0];
3948 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
3949 fPathPts.append(verb, &pts[1]);
3950 }
3951 } while (verb != SkPath::kDone_Verb);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003952 return fPathVerbs.count() - 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003953}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003954
caryclark@google.com235f56a2012-09-14 14:19:30 +00003955void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003956 SkPath::Verb reducedVerb;
3957 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003958 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003959 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003960 const SkPoint* finalCurveStart = NULL;
3961 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003962 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003963 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
3964 switch (verb) {
3965 case SkPath::kMove_Verb:
3966 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003967 if (!fCurrentContour) {
3968 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003969 fCurrentContour->setOperand(fOperand);
3970 fCurrentContour->setXor(fXorMask == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003971 *fExtra.append() = -1; // start new contour
3972 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003973 finalCurveEnd = pointsPtr++;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003974 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003975 case SkPath::kLine_Verb:
3976 // skip degenerate points
3977 if (pointsPtr[-1].fX != pointsPtr[0].fX
3978 || pointsPtr[-1].fY != pointsPtr[0].fY) {
3979 fCurrentContour->addLine(&pointsPtr[-1]);
3980 }
3981 break;
3982 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00003983
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003984 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
3985 if (reducedVerb == 0) {
3986 break; // skip degenerate points
3987 }
3988 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00003989 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003990 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003991 break;
3992 }
3993 fCurrentContour->addQuad(&pointsPtr[-1]);
3994 break;
3995 case SkPath::kCubic_Verb:
3996 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
3997 if (reducedVerb == 0) {
3998 break; // skip degenerate points
3999 }
4000 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004001 *fExtra.append() =
4002 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004003 break;
4004 }
4005 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004006 *fExtra.append() =
4007 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004008 break;
4009 }
4010 fCurrentContour->addCubic(&pointsPtr[-1]);
4011 break;
4012 case SkPath::kClose_Verb:
4013 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004014 if (finalCurveStart && finalCurveEnd
4015 && *finalCurveStart != *finalCurveEnd) {
4016 *fReducePts.append() = *finalCurveStart;
4017 *fReducePts.append() = *finalCurveEnd;
4018 *fExtra.append() =
4019 fCurrentContour->addLine(fReducePts.end() - 2);
4020 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004021 complete();
caryclark@google.com31143cf2012-11-09 22:14:19 +00004022 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004023 default:
4024 SkDEBUGFAIL("bad verb");
4025 return;
4026 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004027 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004028 pointsPtr += verb;
4029 SkASSERT(fCurrentContour);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004030 nextVerb:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004031 if (verbPtr == endOfFirstHalf) {
4032 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004033 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004034 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004035}
4036
4037private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004038 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004039 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004040 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004041 Contour* fCurrentContour;
4042 SkTArray<Contour>& fContours;
4043 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004044 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com235f56a2012-09-14 14:19:30 +00004045 ShapeOpMask fXorMask;
4046 int fSecondHalf;
4047 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004048};
4049
4050class Work {
4051public:
4052 enum SegmentType {
4053 kHorizontalLine_Segment = -1,
4054 kVerticalLine_Segment = 0,
4055 kLine_Segment = SkPath::kLine_Verb,
4056 kQuad_Segment = SkPath::kQuad_Verb,
4057 kCubic_Segment = SkPath::kCubic_Verb,
4058 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004059
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004060 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4061 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4062 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004063
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004064 // FIXME: does it make sense to write otherIndex now if we're going to
4065 // fix it up later?
4066 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004067 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004068 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004069
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004070 // Avoid collapsing t values that are close to the same since
4071 // we walk ts to describe consecutive intersections. Since a pair of ts can
4072 // be nearly equal, any problems caused by this should be taken care
4073 // of later.
4074 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004075 int addT(double newT, const Work& other) {
4076 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004077 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004078
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004079 bool advance() {
4080 return ++fIndex < fLast;
4081 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004082
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004083 SkScalar bottom() const {
4084 return bounds().fBottom;
4085 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004086
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004087 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004088 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004089 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004090
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004091 const SkPoint* cubic() const {
4092 return fCubic;
4093 }
4094
4095 void init(Contour* contour) {
4096 fContour = contour;
4097 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004098 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004099 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004100
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004101 bool isAdjacent(const Work& next) {
4102 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4103 }
4104
4105 bool isFirstLast(const Work& next) {
4106 return fContour == next.fContour && fIndex == 0
4107 && next.fIndex == fLast - 1;
4108 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004109
4110 SkScalar left() const {
4111 return bounds().fLeft;
4112 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004113
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004114 void promoteToCubic() {
4115 fCubic[0] = pts()[0];
4116 fCubic[2] = pts()[1];
4117 fCubic[3] = pts()[2];
4118 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4119 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4120 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4121 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4122 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004123
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004124 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004125 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004126 }
4127
4128 SkScalar right() const {
4129 return bounds().fRight;
4130 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004131
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004132 ptrdiff_t segmentIndex() const {
4133 return fIndex;
4134 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004135
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004136 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004137 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004138 SegmentType type = (SegmentType) segment.verb();
4139 if (type != kLine_Segment) {
4140 return type;
4141 }
4142 if (segment.isHorizontal()) {
4143 return kHorizontalLine_Segment;
4144 }
4145 if (segment.isVertical()) {
4146 return kVerticalLine_Segment;
4147 }
4148 return kLine_Segment;
4149 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004150
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004151 bool startAfter(const Work& after) {
4152 fIndex = after.fIndex;
4153 return advance();
4154 }
4155
4156 SkScalar top() const {
4157 return bounds().fTop;
4158 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004159
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004160 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004161 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004162 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004163
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004164 SkScalar x() const {
4165 return bounds().fLeft;
4166 }
4167
4168 bool xFlipped() const {
4169 return x() != pts()[0].fX;
4170 }
4171
4172 SkScalar y() const {
4173 return bounds().fTop;
4174 }
4175
4176 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004177 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004178 }
4179
4180protected:
4181 Contour* fContour;
4182 SkPoint fCubic[4];
4183 int fIndex;
4184 int fLast;
4185};
4186
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004187#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004188static void debugShowLineIntersection(int pts, const Work& wt,
4189 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004190 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004191 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004192 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4193 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4194 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4195 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004196 return;
4197 }
4198 SkPoint wtOutPt, wnOutPt;
4199 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4200 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004201 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 +00004202 __FUNCTION__,
4203 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4204 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4205 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004206 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004207 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004208 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004209 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4210 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4211 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004212 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4213 }
4214 SkDebugf("\n");
4215}
4216
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004217static void debugShowQuadLineIntersection(int pts, const Work& wt,
4218 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4219 if (!pts) {
4220 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00004221 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004222 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4223 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004224 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004225 return;
4226 }
4227 SkPoint wtOutPt, wnOutPt;
4228 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4229 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4230 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4231 __FUNCTION__,
4232 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4233 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4234 wtOutPt.fX, wtOutPt.fY);
4235 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004236 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
4237 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004238 }
4239 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4240 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4241 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4242 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004243 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
4244 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004245 }
4246 SkDebugf("\n");
4247}
4248
caryclark@google.coma461ff02012-10-11 12:54:23 +00004249static void debugShowQuadIntersection(int pts, const Work& wt,
4250 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4251 if (!pts) {
4252 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
4253 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
4254 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00004255 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00004256 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004257 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00004258 return;
4259 }
4260 SkPoint wtOutPt, wnOutPt;
4261 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4262 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4263 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4264 __FUNCTION__,
4265 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4266 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4267 wtOutPt.fX, wtOutPt.fY);
4268 if (pts == 2) {
4269 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
4270 }
4271 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4272 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4273 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
4274 wnOutPt.fX, wnOutPt.fY);
4275 if (pts == 2) {
4276 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004277 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004278 SkDebugf("\n");
4279}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004280#else
4281static void debugShowLineIntersection(int , const Work& ,
4282 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004283}
caryclark@google.coma461ff02012-10-11 12:54:23 +00004284
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004285static void debugShowQuadLineIntersection(int , const Work& ,
4286 const Work& , const double [2], const double [2]) {
4287}
4288
caryclark@google.coma461ff02012-10-11 12:54:23 +00004289static void debugShowQuadIntersection(int , const Work& ,
4290 const Work& , const double [2], const double [2]) {
4291}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004292#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004293
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004294static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004295
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004296 if (test != next) {
4297 if (test->bounds().fBottom < next->bounds().fTop) {
4298 return false;
4299 }
4300 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
4301 return true;
4302 }
4303 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004304 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004305 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004306 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004307 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004308 Work wn;
4309 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004310 if (test == next && !wn.startAfter(wt)) {
4311 continue;
4312 }
4313 do {
4314 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
4315 continue;
4316 }
4317 int pts;
4318 Intersections ts;
4319 bool swap = false;
4320 switch (wt.segmentType()) {
4321 case Work::kHorizontalLine_Segment:
4322 swap = true;
4323 switch (wn.segmentType()) {
4324 case Work::kHorizontalLine_Segment:
4325 case Work::kVerticalLine_Segment:
4326 case Work::kLine_Segment: {
4327 pts = HLineIntersect(wn.pts(), wt.left(),
4328 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004329 debugShowLineIntersection(pts, wt, wn,
4330 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004331 break;
4332 }
4333 case Work::kQuad_Segment: {
4334 pts = HQuadIntersect(wn.pts(), wt.left(),
4335 wt.right(), wt.y(), wt.xFlipped(), ts);
4336 break;
4337 }
4338 case Work::kCubic_Segment: {
4339 pts = HCubicIntersect(wn.pts(), wt.left(),
4340 wt.right(), wt.y(), wt.xFlipped(), ts);
4341 break;
4342 }
4343 default:
4344 SkASSERT(0);
4345 }
4346 break;
4347 case Work::kVerticalLine_Segment:
4348 swap = true;
4349 switch (wn.segmentType()) {
4350 case Work::kHorizontalLine_Segment:
4351 case Work::kVerticalLine_Segment:
4352 case Work::kLine_Segment: {
4353 pts = VLineIntersect(wn.pts(), wt.top(),
4354 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004355 debugShowLineIntersection(pts, wt, wn,
4356 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004357 break;
4358 }
4359 case Work::kQuad_Segment: {
4360 pts = VQuadIntersect(wn.pts(), wt.top(),
4361 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4362 break;
4363 }
4364 case Work::kCubic_Segment: {
4365 pts = VCubicIntersect(wn.pts(), wt.top(),
4366 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4367 break;
4368 }
4369 default:
4370 SkASSERT(0);
4371 }
4372 break;
4373 case Work::kLine_Segment:
4374 switch (wn.segmentType()) {
4375 case Work::kHorizontalLine_Segment:
4376 pts = HLineIntersect(wt.pts(), wn.left(),
4377 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004378 debugShowLineIntersection(pts, wt, wn,
4379 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004380 break;
4381 case Work::kVerticalLine_Segment:
4382 pts = VLineIntersect(wt.pts(), wn.top(),
4383 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004384 debugShowLineIntersection(pts, wt, wn,
4385 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004386 break;
4387 case Work::kLine_Segment: {
4388 pts = LineIntersect(wt.pts(), wn.pts(), ts);
4389 debugShowLineIntersection(pts, wt, wn,
4390 ts.fT[1], ts.fT[0]);
4391 break;
4392 }
4393 case Work::kQuad_Segment: {
4394 swap = true;
4395 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004396 debugShowQuadLineIntersection(pts, wn, wt,
4397 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004398 break;
4399 }
4400 case Work::kCubic_Segment: {
4401 swap = true;
4402 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
4403 break;
4404 }
4405 default:
4406 SkASSERT(0);
4407 }
4408 break;
4409 case Work::kQuad_Segment:
4410 switch (wn.segmentType()) {
4411 case Work::kHorizontalLine_Segment:
4412 pts = HQuadIntersect(wt.pts(), wn.left(),
4413 wn.right(), wn.y(), wn.xFlipped(), ts);
4414 break;
4415 case Work::kVerticalLine_Segment:
4416 pts = VQuadIntersect(wt.pts(), wn.top(),
4417 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4418 break;
4419 case Work::kLine_Segment: {
4420 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004421 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004422 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004423 break;
4424 }
4425 case Work::kQuad_Segment: {
4426 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004427 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004428 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004429 break;
4430 }
4431 case Work::kCubic_Segment: {
4432 wt.promoteToCubic();
4433 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
4434 break;
4435 }
4436 default:
4437 SkASSERT(0);
4438 }
4439 break;
4440 case Work::kCubic_Segment:
4441 switch (wn.segmentType()) {
4442 case Work::kHorizontalLine_Segment:
4443 pts = HCubicIntersect(wt.pts(), wn.left(),
4444 wn.right(), wn.y(), wn.xFlipped(), ts);
4445 break;
4446 case Work::kVerticalLine_Segment:
4447 pts = VCubicIntersect(wt.pts(), wn.top(),
4448 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4449 break;
4450 case Work::kLine_Segment: {
4451 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
4452 break;
4453 }
4454 case Work::kQuad_Segment: {
4455 wn.promoteToCubic();
4456 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
4457 break;
4458 }
4459 case Work::kCubic_Segment: {
4460 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4461 break;
4462 }
4463 default:
4464 SkASSERT(0);
4465 }
4466 break;
4467 default:
4468 SkASSERT(0);
4469 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004470 if (!foundCommonContour && pts > 0) {
4471 test->addCross(next);
4472 next->addCross(test);
4473 foundCommonContour = true;
4474 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004475 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004476 if (pts == 2) {
4477 if (wn.segmentType() <= Work::kLine_Segment
4478 && wt.segmentType() <= Work::kLine_Segment) {
4479 wt.addCoincident(wn, ts, swap);
4480 continue;
4481 }
4482 if (wn.segmentType() == Work::kQuad_Segment
4483 && wt.segmentType() == Work::kQuad_Segment
4484 && ts.coincidentUsed() == 2) {
4485 wt.addCoincident(wn, ts, swap);
4486 continue;
4487 }
4488
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004489 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004490 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004491 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4492 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004493 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4494 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004495 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4496 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004497 }
4498 } while (wn.advance());
4499 } while (wt.advance());
4500 return true;
4501}
4502
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004503// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004504// see if coincidence is formed by clipping non-concident segments
caryclark@google.com235f56a2012-09-14 14:19:30 +00004505static void coincidenceCheck(SkTDArray<Contour*>& contourList) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004506 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004507 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004508 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004509 contour->resolveCoincidence();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004510 }
4511 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4512 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004513 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004514 }
4515}
4516
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004517// project a ray from the top of the contour up and see if it hits anything
4518// note: when we compute line intersections, we keep track of whether
4519// two contours touch, so we need only look at contours not touching this one.
4520// OPTIMIZATION: sort contourList vertically to avoid linear walk
4521static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com31143cf2012-11-09 22:14:19 +00004522 const Segment* current, int index, int endIndex, bool opp) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004523 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004524 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004525 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00004526 const Segment* test = NULL;
4527 int tIndex;
4528 double tHit;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004529 for (int cTest = 0; cTest < contourCount; ++cTest) {
4530 Contour* contour = contourList[cTest];
caryclark@google.com31143cf2012-11-09 22:14:19 +00004531 if (contour->operand() ^ current->operand() != opp) {
4532 continue;
4533 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004534 if (basePt.fY < contour->bounds().fTop) {
4535 continue;
4536 }
4537 if (bestY > contour->bounds().fBottom) {
4538 continue;
4539 }
caryclark@google.com47580692012-07-23 12:14:49 +00004540 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
4541 if (next) {
4542 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004543 }
caryclark@google.com47580692012-07-23 12:14:49 +00004544 }
4545 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00004546 return 0;
4547 }
4548 int winding, windValue;
4549 // If the ray hit the end of a span, we need to construct the wheel of
4550 // angles to find the span closest to the ray -- even if there are just
4551 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004552 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004553 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00004554 SkTDArray<Angle> angles;
4555 int end = test->nextSpan(tIndex, 1);
4556 if (end < 0) {
4557 end = test->nextSpan(tIndex, -1);
4558 }
4559 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00004560 SkASSERT(angles.count() > 0);
4561 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
4562#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00004563 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00004564#endif
4565 return 0;
4566 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004567 test->buildAngles(tIndex, angles, false);
caryclark@google.com47580692012-07-23 12:14:49 +00004568 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00004569 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00004570 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004571 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00004572 // hour after 6 o'clock
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004573 (void) Segment::SortAngles(angles, sorted);
caryclark@google.com47580692012-07-23 12:14:49 +00004574#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004575 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00004576#endif
4577 // walk the sorted angle fan to find the lowest angle
4578 // above the base point. Currently, the first angle in the sorted array
4579 // is 12 noon or an earlier hour (the next counterclockwise)
4580 int count = sorted.count();
4581 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004582 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00004583 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004584 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00004585 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00004586 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004587 if (angle->unsortable()) {
4588 continue;
4589 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004590 if (baseMatches && angle->isHorizontal()) {
4591 continue;
4592 }
4593 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00004594 test = angle->segment();
4595 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
4596 const SkPoint* pts = test->pts();
4597 indexDx = pts[2].fX - pts[1].fX - indexDx;
4598 }
caryclark@google.com47580692012-07-23 12:14:49 +00004599 if (indexDx < 0) {
4600 left = index;
4601 } else if (indexDx > 0) {
4602 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00004603 int previous = index - 1;
4604 if (previous < 0) {
4605 previous = count - 1;
4606 }
4607 const Angle* prev = sorted[previous];
4608 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
4609#if DEBUG_SORT
4610 SkDebugf("%s use prev\n", __FUNCTION__);
4611#endif
4612 right = previous;
4613 }
caryclark@google.com47580692012-07-23 12:14:49 +00004614 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004615 } else {
4616 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004617 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004618 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004619 if (left < 0 && right < 0) {
4620 left = mid;
4621 }
caryclark@google.com47580692012-07-23 12:14:49 +00004622 SkASSERT(left >= 0 || right >= 0);
4623 if (left < 0) {
4624 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004625 } else if (left >= 0 && mid >= 0 && right >= 0
4626 && sorted[mid]->sign() == sorted[right]->sign()) {
4627 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004628 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004629 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004630 test = angle->segment();
4631 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004632 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004633 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004634#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004635 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4636 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004637#endif
4638 } else {
4639 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004640 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004641 windValue = test->windValue(tIndex);
4642#if DEBUG_WINDING
4643 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4644 windValue);
4645#endif
4646 }
4647 // see if a + change in T results in a +/- change in X (compute x'(T))
4648 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004649 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4650 const SkPoint* pts = test->pts();
4651 dx = pts[2].fX - pts[1].fX - dx;
4652 }
caryclark@google.com47580692012-07-23 12:14:49 +00004653#if DEBUG_WINDING
4654 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4655#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004656 SkASSERT(dx != 0);
4657 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004658 winding += dx > 0 ? -windValue : windValue;
4659#if DEBUG_WINDING
4660 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4661#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004662 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004663 return winding;
4664}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004665
caryclark@google.com24bec792012-08-20 12:43:57 +00004666static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4667 int contourCount = contourList.count();
4668 Segment* result;
4669 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4670 Contour* contour = contourList[cIndex];
4671 result = contour->undoneSegment(start, end);
4672 if (result) {
4673 return result;
4674 }
4675 }
4676 return NULL;
4677}
4678
4679
4680
caryclark@google.com31143cf2012-11-09 22:14:19 +00004681static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004682 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004683 Span* span;
4684 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004685 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4686 Segment* segment = backPtr.fOther;
4687 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004688 SkTDArray<Angle> angles;
4689 int done = 0;
4690 if (segment->activeAngle(tIndex, done, angles)) {
4691 Angle* last = angles.end() - 1;
4692 tIndex = last->start();
4693 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00004694 #if TRY_ROTATE
4695 *chase.insert(0) = span;
4696 #else
4697 *chase.append() = span;
4698 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004699 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004700 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004701 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004702 continue;
4703 }
4704 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004705 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004706#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004707 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004708#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004709 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004710 continue;
4711 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004712 // find first angle, initialize winding to computed fWindSum
4713 int firstIndex = -1;
4714 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004715 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004716 do {
4717 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004718 segment = angle->segment();
4719 winding = segment->windSum(angle);
4720 } while (winding == SK_MinS32);
4721 int spanWinding = segment->spanSign(angle->start(), angle->end());
4722 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00004723 SkDebugf("%s winding=%d spanWinding=%d\n",
4724 __FUNCTION__, winding, spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00004725 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00004726 // turn span winding into contour winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004727 if (spanWinding * winding < 0) {
4728 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004729 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004730 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004731 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004732 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004733 // we care about first sign and whether wind sum indicates this
4734 // edge is inside or outside. Maybe need to pass span winding
4735 // or first winding or something into this function?
4736 // advance to first undone angle, then return it and winding
4737 // (to set whether edges are active or not)
4738 int nextIndex = firstIndex + 1;
4739 int angleCount = sorted.count();
4740 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004741 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00004742 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00004743 do {
4744 SkASSERT(nextIndex != firstIndex);
4745 if (nextIndex == angleCount) {
4746 nextIndex = 0;
4747 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004748 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00004749 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004750 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00004751 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004752 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00004753 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
4754 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004755 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004756 tIndex = angle->start();
4757 endIndex = angle->end();
4758 int lesser = SkMin32(tIndex, endIndex);
4759 const Span& nextSpan = segment->span(lesser);
4760 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004761#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00004762 // FIXME: this be wrong. assign startWinding if edge is in
4763 // same direction. If the direction is opposite, winding to
4764 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00004765 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004766 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004767 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004768 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004769#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004770 break;
4771 }
4772 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004773 #if TRY_ROTATE
4774 *chase.insert(0) = span;
4775 #else
4776 *chase.append() = span;
4777 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004778 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004779 }
4780 return NULL;
4781}
4782
caryclark@google.com027de222012-07-12 12:52:50 +00004783#if DEBUG_ACTIVE_SPANS
4784static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004785 int index;
4786 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00004787 contourList[index]->debugShowActiveSpans();
4788 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004789 for (index = 0; index < contourList.count(); ++ index) {
4790 contourList[index]->validateActiveSpans();
4791 }
caryclark@google.com027de222012-07-12 12:52:50 +00004792}
4793#endif
4794
caryclark@google.com27c449a2012-07-27 18:26:38 +00004795static bool windingIsActive(int winding, int spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004796 // FIXME: !spanWinding test must be superflorous, true?
caryclark@google.com27c449a2012-07-27 18:26:38 +00004797 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
4798 && (!winding || !spanWinding || winding == -spanWinding);
4799}
4800
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004801static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comf839c032012-10-26 21:03:50 +00004802 int& endIndex, SkPoint& topLeft) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004803 Segment* result;
4804 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00004805 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004806 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00004807 Segment* topStart = NULL;
4808 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4809 Contour* contour = contourList[cIndex];
4810 const Bounds& bounds = contour->bounds();
4811 if (bounds.fBottom < topLeft.fY) {
4812 continue;
4813 }
4814 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
4815 continue;
4816 }
4817 Segment* test = contour->topSortableSegment(topLeft, bestXY);
4818 if (test) {
4819 topStart = test;
4820 }
4821 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004822 if (!topStart) {
4823 return NULL;
4824 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004825 topLeft = bestXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004826 result = topStart->findTop(index, endIndex);
4827 } while (!result);
4828 return result;
4829}
caryclark@google.com31143cf2012-11-09 22:14:19 +00004830
4831static int updateWindings(const Segment* current, int index, int endIndex,
4832 int& spanWinding, int* oppWinding) {
4833 int lesser = SkMin32(index, endIndex);
4834 spanWinding = current->spanSign(index, endIndex);
4835 int winding = current->windSum(lesser);
4836 bool inner = useInnerWinding(winding - spanWinding, winding);
4837#if DEBUG_WINDING
4838 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
4839 " inner=%d result=%d\n",
4840 __FUNCTION__, current->debugID(), current->t(lesser),
4841 spanWinding, winding, SkSign32(index - endIndex),
4842 useInnerWinding(winding - spanWinding, winding),
4843 inner ? winding - spanWinding : winding);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004844#endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00004845 if (inner) {
4846 winding -= spanWinding;
4847 }
4848 if (oppWinding) {
4849 *oppWinding = current->oppSum(lesser);
4850 }
4851 return winding;
4852}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004853
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004854// Each segment may have an inside or an outside. Segments contained within
4855// winding may have insides on either side, and form a contour that should be
4856// ignored. Segments that are coincident with opposing direction segments may
4857// have outsides on either side, and should also disappear.
4858// 'Normal' segments will have one inside and one outside. Subsequent connections
4859// when winding should follow the intersection direction. If more than one edge
4860// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004861 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00004862 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004863// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00004864static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004865 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004866 bool unsortable = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00004867 bool closable = true;
4868 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com15fa1382012-05-07 20:49:36 +00004869 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00004870 int index, endIndex;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004871 // iterates while top is unsortable
caryclark@google.comf839c032012-10-26 21:03:50 +00004872 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004873 if (!current) {
4874 break;
4875 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004876 int contourWinding;
4877 if (firstContour) {
4878 contourWinding = 0;
4879 firstContour = false;
4880 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004881 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004882 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00004883 if (sumWinding == SK_MinS32) {
4884 sumWinding = current->computeSum(index, endIndex);
4885 }
4886 if (sumWinding == SK_MinS32) {
4887 contourWinding = innerContourCheck(contourList, current,
caryclark@google.com31143cf2012-11-09 22:14:19 +00004888 index, endIndex, false);
caryclark@google.com200c2112012-08-03 15:05:04 +00004889 } else {
4890 contourWinding = sumWinding;
4891 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004892 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
4893 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00004894 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004895 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004896#if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00004897 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n",
4898 __FUNCTION__, sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00004899 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004900#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004901 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004902#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004903 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004904 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
4905#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004906 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004907 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004908 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004909 // FIXME: needs work. While it works in limited situations, it does
4910 // not always compute winding correctly. Active should be removed and instead
4911 // the initial winding should be correctly passed in so that if the
4912 // inner contour is wound the same way, it never finds an accumulated
4913 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00004914 // other than zero when resolving sorted angles.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004915 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004916 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004917 bool active = windingIsActive(winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004918 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00004919 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
4920 __FUNCTION__, active ? "true" : "false",
4921 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004922 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004923 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004924 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004925 int nextStart = index;
4926 int nextEnd = endIndex;
4927 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004928 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004929 if (!next) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004930 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00004931 && current->verb() != SkPath::kLine_Verb
4932 && !simple.isClosed()) {
4933 current->addCurveTo(index, endIndex, simple, true);
4934 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00004935 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004936 break;
4937 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004938 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004939 current = next;
4940 index = nextStart;
4941 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00004942 } while (!simple.isClosed()
4943 && ((active && !unsortable) || !current->done()));
4944 if (active) {
4945 if (!simple.isClosed()) {
4946 SkASSERT(unsortable);
4947 int min = SkMin32(index, endIndex);
4948 if (!current->done(min)) {
4949 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004950 current->markDone(SkMin32(index, endIndex),
4951 winding ? winding : spanWinding);
caryclark@google.comf839c032012-10-26 21:03:50 +00004952 }
4953 closable = false;
4954 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004955 simple.close();
4956 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004957 current = findChase(chaseArray, index, endIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004958 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00004959 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004960 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004961 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00004962 break;
4963 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004964 winding = updateWindings(current, index, endIndex, spanWinding, NULL);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004965 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004966 } while (true);
caryclark@google.comf839c032012-10-26 21:03:50 +00004967 return closable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004968}
4969
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004970// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00004971static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00004972 Segment* current;
4973 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004974 bool unsortable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00004975 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00004976 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004977 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004978 int nextStart = start;
4979 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004980 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00004981 if (!next) {
caryclark@google.comf839c032012-10-26 21:03:50 +00004982 if (simple.hasMove()
4983 && current->verb() != SkPath::kLine_Verb
4984 && !simple.isClosed()) {
4985 current->addCurveTo(start, end, simple, true);
4986 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00004987 }
caryclark@google.com24bec792012-08-20 12:43:57 +00004988 break;
4989 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004990 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00004991 current = next;
4992 start = nextStart;
4993 end = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00004994 } while (!simple.isClosed());
4995 // FIXME: add unsortable test
4996 if (simple.hasMove()) {
caryclark@google.com24bec792012-08-20 12:43:57 +00004997 simple.close();
4998 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004999 #if DEBUG_ACTIVE_SPANS
5000 debugShowActiveSpans(contourList);
5001 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005002 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005003 return !unsortable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005004}
5005
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005006static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5007 int contourCount = contourList.count();
5008 for (int cTest = 0; cTest < contourCount; ++cTest) {
5009 Contour* contour = contourList[cTest];
5010 contour->fixOtherTIndex();
5011 }
5012}
5013
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005014static void sortSegments(SkTDArray<Contour*>& contourList) {
5015 int contourCount = contourList.count();
5016 for (int cTest = 0; cTest < contourCount; ++cTest) {
5017 Contour* contour = contourList[cTest];
5018 contour->sortSegments();
5019 }
5020}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005021
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005022static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005023 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005024 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005025 if (count == 0) {
5026 return;
5027 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005028 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005029 *list.append() = &contours[index];
5030 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005031 QSort<Contour>(list.begin(), list.end() - 1);
5032}
5033
caryclark@google.comf839c032012-10-26 21:03:50 +00005034static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005035 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005036}
5037
caryclark@google.comf839c032012-10-26 21:03:50 +00005038 /*
5039 check start and end of each contour
5040 if not the same, record them
5041 match them up
5042 connect closest
5043 reassemble contour pieces into new path
5044 */
5045static void assemble(const PathWrapper& path, PathWrapper& simple) {
5046#if DEBUG_PATH_CONSTRUCTION
5047 SkDebugf("%s\n", __FUNCTION__);
5048#endif
5049 SkTArray<Contour> contours;
5050 EdgeBuilder builder(path, contours);
5051 builder.finish();
5052 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005053 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005054 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00005055 for (outer = 0; outer < count; ++outer) {
5056 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005057 const SkPoint& eStart = eContour.start();
5058 const SkPoint& eEnd = eContour.end();
5059 if (approximatelyEqual(eStart, eEnd)) {
5060 eContour.toPath(simple);
5061 continue;
5062 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005063 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005064 }
5065 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005066 if (count == 0) {
5067 return;
5068 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005069 SkTDArray<int> sLink, eLink;
5070 sLink.setCount(count);
5071 eLink.setCount(count);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005072 SkTDArray<double> sBest, eBest;
5073 sBest.setCount(count);
5074 eBest.setCount(count);
caryclark@google.comf839c032012-10-26 21:03:50 +00005075 int rIndex;
5076 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005077 outer = runs[rIndex];
5078 const Contour& oContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005079 const SkPoint& oStart = oContour.start();
5080 const SkPoint& oEnd = oContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005081 double dx = oEnd.fX - oStart.fX;
5082 double dy = oEnd.fY - oStart.fY;
5083 double dist = dx * dx + dy * dy;
5084 sBest[rIndex] = eBest[rIndex] = dist;
5085 sLink[rIndex] = eLink[rIndex] = rIndex;
5086 }
5087 for (rIndex = 0; rIndex < count - 1; ++rIndex) {
5088 outer = runs[rIndex];
5089 const Contour& oContour = contours[outer];
5090 const SkPoint& oStart = oContour.start();
5091 const SkPoint& oEnd = oContour.end();
5092 double bestStartDist = sBest[rIndex];
5093 double bestEndDist = eBest[rIndex];
5094 for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) {
5095 int inner = runs[iIndex];
5096 const Contour& iContour = contours[inner];
caryclark@google.comf839c032012-10-26 21:03:50 +00005097 const SkPoint& iStart = iContour.start();
5098 const SkPoint& iEnd = iContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005099 double dx = iStart.fX - oStart.fX;
5100 double dy = iStart.fY - oStart.fY;
5101 double dist = dx * dx + dy * dy;
5102 if (bestStartDist > dist) {
5103 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005104 sLink[rIndex] = ~iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005105 sLink[iIndex] = ~rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005106 }
5107 dx = iEnd.fX - oStart.fX;
5108 dy = iEnd.fY - oStart.fY;
5109 dist = dx * dx + dy * dy;
5110 if (bestStartDist > dist) {
5111 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005112 sLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005113 eLink[iIndex] = rIndex;
5114 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005115 dx = iStart.fX - oEnd.fX;
5116 dy = iStart.fY - oEnd.fY;
5117 dist = dx * dx + dy * dy;
5118 if (bestEndDist > dist) {
5119 bestEndDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005120 sLink[iIndex] = rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005121 eLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005122 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005123 dx = iEnd.fX - oEnd.fX;
5124 dy = iEnd.fY - oEnd.fY;
5125 dist = dx * dx + dy * dy;
5126 if (bestEndDist > dist) {
5127 bestEndDist = dist;
5128 eLink[iIndex] = ~rIndex;
5129 eLink[rIndex] = ~iIndex;
5130 }
5131 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005132 }
5133 rIndex = 0;
5134 bool forward = true;
5135 bool first = true;
5136 const SkPoint* startPtr;
5137 int sIndex = sLink[rIndex];
caryclark@google.com0b7da432012-10-31 19:00:20 +00005138 SkASSERT(sIndex != INT_MAX);
5139 sLink[rIndex] = INT_MAX;
5140 int eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005141 if (sIndex < 0) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005142 eIndex = sLink[~sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005143 sLink[~sIndex] = INT_MAX;
5144 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005145 eIndex = eLink[sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005146 eLink[sIndex] = INT_MAX;
5147 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005148 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005149 do {
5150 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005151 outer = runs[rIndex];
5152 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005153 if (first) {
5154 startPtr = &contour.start();
5155 first = false;
5156 simple.deferredMove(startPtr[0]);
5157 }
5158 const SkPoint* endPtr;
5159 if (forward) {
5160 contour.toPartialForward(simple);
5161 endPtr = &contour.end();
5162 } else {
5163 contour.toPartialBackward(simple);
5164 endPtr = &contour.start();
5165 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005166 if (sIndex == eIndex) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005167 simple.close();
5168 first = forward = true;
5169 break;
5170 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005171 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005172 eIndex = eLink[rIndex];
5173 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005174 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005175 if (eIndex >= 0) {
5176 SkASSERT(sLink[eIndex] == rIndex);
5177 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005178 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005179 SkASSERT(eLink[~eIndex] == ~rIndex);
5180 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005181 }
5182 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005183 eIndex = sLink[rIndex];
5184 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005185 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005186 if (eIndex >= 0) {
5187 SkASSERT(eLink[eIndex] == rIndex);
5188 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005189 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005190 SkASSERT(sLink[~eIndex] == ~rIndex);
5191 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005192 }
5193 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005194 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005195 if (rIndex < 0) {
5196 forward ^= 1;
5197 rIndex = ~rIndex;
5198 }
5199 } while (true);
5200 for (rIndex = 0; rIndex < count; ++rIndex) {
5201 if (sLink[rIndex] != INT_MAX) {
5202 break;
5203 }
5204 }
5205 } while (rIndex < count);
5206 SkASSERT(first);
5207}
5208
5209void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005210 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00005211 result.reset();
5212 result.setFillType(SkPath::kEvenOdd_FillType);
5213 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005214
5215 // turn path into list of segments
5216 SkTArray<Contour> contours;
5217 // FIXME: add self-intersecting cubics' T values to segment
5218 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00005219 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005220 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005221 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005222 Contour** currentPtr = contourList.begin();
5223 if (!currentPtr) {
5224 return;
5225 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005226 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005227 // find all intersections between segments
5228 do {
5229 Contour** nextPtr = currentPtr;
5230 Contour* current = *currentPtr++;
5231 Contour* next;
5232 do {
5233 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005234 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005235 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005236 // eat through coincident edges
caryclark@google.com235f56a2012-09-14 14:19:30 +00005237 coincidenceCheck(contourList);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00005238 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005239 sortSegments(contourList);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005240#if DEBUG_ACTIVE_SPANS
5241 debugShowActiveSpans(contourList);
5242#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005243 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005244 if (builder.xorMask() == kWinding_Mask
5245 ? !bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00005246 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005247 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00005248 SkPath temp;
5249 temp.setFillType(SkPath::kEvenOdd_FillType);
5250 PathWrapper assembled(temp);
5251 assemble(simple, assembled);
5252 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00005253 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005254}
5255