blob: 2a65ad1219a100611c1157fd963c1d6f31fafa0b [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
caryclark@google.com47580692012-07-23 12:14:49 +000020// An Edge is a Segment generated from a Span
caryclark@google.com15fa1382012-05-07 20:49:36 +000021
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022// FIXME: remove once debugging is complete
caryclark@google.com47580692012-07-23 12:14:49 +000023#ifdef SK_DEBUG
24int gDebugMaxWindSum = SK_MaxS32;
25int gDebugMaxWindValue = SK_MaxS32;
26#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +000027
caryclark@google.com3350c3c2012-08-24 15:24:36 +000028#define HIGH_DEF_ANGLES 1
29
30#if HIGH_DEF_ANGLES
31typedef double AngleValue;
32#else
33typedef SkScalar AngleValue;
34#endif
35
caryclark@google.com47580692012-07-23 12:14:49 +000036#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa0588f2012-04-26 21:01:06 +000037
caryclark@google.com32546db2012-08-31 20:55:07 +000038#if 1 // set to 1 for multiple thread -- no debugging
caryclark@google.com47580692012-07-23 12:14:49 +000039
40const bool gRunTestsInOneThread = false;
41
42#define DEBUG_ACTIVE_SPANS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000043#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000044#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000045#define DEBUG_ANGLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000046#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000047#define DEBUG_CROSS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000048#define DEBUG_DUMP 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000049#define DEBUG_MARK_DONE 0
caryclark@google.com47580692012-07-23 12:14:49 +000050#define DEBUG_PATH_CONSTRUCTION 0
51#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000052#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000053#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000054
55#else
56
caryclark@google.com47580692012-07-23 12:14:49 +000057const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000058
caryclark@google.comafe56de2012-07-24 18:11:03 +000059#define DEBUG_ACTIVE_SPANS 1
caryclark@google.comc899ad92012-08-23 15:24:42 +000060#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com24bec792012-08-20 12:43:57 +000061#define DEBUG_ADD_T_PAIR 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000062#define DEBUG_ANGLE 1
63#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000064#define DEBUG_CROSS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000065#define DEBUG_DUMP 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000066#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000067#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000068#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000069#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000070#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000071
72#endif
73
caryclark@google.com534aa5b2012-08-02 20:08:21 +000074#if (DEBUG_ACTIVE_SPANS || DEBUG_CONCIDENT || DEBUG_SORT) && !DEBUG_DUMP
caryclark@google.com027de222012-07-12 12:52:50 +000075#undef DEBUG_DUMP
76#define DEBUG_DUMP 1
77#endif
78
caryclark@google.comfa0588f2012-04-26 21:01:06 +000079#if DEBUG_DUMP
80static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000081// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000082static int gContourID;
83static int gSegmentID;
84#endif
85
caryclark@google.com8dcf1142012-07-02 20:27:02 +000086#ifndef DEBUG_TEST
87#define DEBUG_TEST 0
88#endif
89
caryclark@google.com32546db2012-08-31 20:55:07 +000090#define MAKE_CONST_LINE(line, pts) \
91 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
92#define MAKE_CONST_QUAD(quad, pts) \
93 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
94 {pts[2].fX, pts[2].fY}}
95#define MAKE_CONST_CUBIC(cubic, pts) \
96 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
97 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
98
caryclark@google.comfa0588f2012-04-26 21:01:06 +000099static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
100 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000101 MAKE_CONST_LINE(aLine, a);
102 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000103 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
104}
105
106static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
107 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000108 MAKE_CONST_QUAD(aQuad, a);
109 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000110 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000111}
112
caryclark@google.com32546db2012-08-31 20:55:07 +0000113static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000114 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000115 MAKE_CONST_CUBIC(aCubic, a);
116 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000117 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
118}
119
120static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
121 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000122 MAKE_CONST_QUAD(aQuad, a);
123 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000124#define TRY_QUARTIC_SOLUTION 1
125#if TRY_QUARTIC_SOLUTION
126 intersect2(aQuad, bQuad, intersections);
127#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000128 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000129#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000130 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000131}
132
133static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
134 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000135 MAKE_CONST_CUBIC(aCubic, a);
136 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000137 intersect(aCubic, bCubic, intersections);
138 return intersections.fUsed;
139}
140
141static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
142 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000143 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000144 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
145}
146
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000147static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
148 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000149 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000150 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
151}
152
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000153static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
154 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000155 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000156 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
157}
158
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000159static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
160 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000161 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000162 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
163}
164
165static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
166 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000167 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000168 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
169}
170
171static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
172 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000173 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000174 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000175}
176
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000177static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
178 SkScalar , SkScalar , bool , Intersections& ) = {
179 NULL,
180 VLineIntersect,
181 VQuadIntersect,
182 VCubicIntersect
183};
184
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000185static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000186 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000187 double x, y;
188 xy_at_t(line, t, x, y);
189 out->fX = SkDoubleToScalar(x);
190 out->fY = SkDoubleToScalar(y);
191}
192
193static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000194 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000195 double x, y;
196 xy_at_t(quad, t, x, y);
197 out->fX = SkDoubleToScalar(x);
198 out->fY = SkDoubleToScalar(y);
199}
200
201static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000202 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000203 double x, y;
204 xy_at_t(cubic, t, x, y);
205 out->fX = SkDoubleToScalar(x);
206 out->fY = SkDoubleToScalar(y);
207}
208
209static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
210 NULL,
211 LineXYAtT,
212 QuadXYAtT,
213 CubicXYAtT
214};
215
216static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000217 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000218 double x;
219 xy_at_t(aLine, t, x, *(double*) 0);
220 return SkDoubleToScalar(x);
221}
222
223static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000224 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000225 double x;
226 xy_at_t(quad, t, x, *(double*) 0);
227 return SkDoubleToScalar(x);
228}
229
230static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000231 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000232 double x;
233 xy_at_t(cubic, t, x, *(double*) 0);
234 return SkDoubleToScalar(x);
235}
236
237static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
238 NULL,
239 LineXAtT,
240 QuadXAtT,
241 CubicXAtT
242};
243
244static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000245 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000246 double y;
247 xy_at_t(aLine, t, *(double*) 0, y);
248 return SkDoubleToScalar(y);
249}
250
251static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000252 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000253 double y;
254 xy_at_t(quad, t, *(double*) 0, y);
255 return SkDoubleToScalar(y);
256}
257
258static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000259 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000260 double y;
261 xy_at_t(cubic, t, *(double*) 0, y);
262 return SkDoubleToScalar(y);
263}
264
265static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
266 NULL,
267 LineYAtT,
268 QuadYAtT,
269 CubicYAtT
270};
271
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000272static SkScalar LineDXAtT(const SkPoint a[2], double ) {
273 return a[1].fX - a[0].fX;
274}
275
276static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000277 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000278 double x;
279 dxdy_at_t(quad, t, x, *(double*) 0);
280 return SkDoubleToScalar(x);
281}
282
283static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000284 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000285 double x;
286 dxdy_at_t(cubic, t, x, *(double*) 0);
287 return SkDoubleToScalar(x);
288}
289
290static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
291 NULL,
292 LineDXAtT,
293 QuadDXAtT,
294 CubicDXAtT
295};
296
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000297static void LineSubDivide(const SkPoint a[2], double startT, double endT,
298 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000299 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000300 _Line dst;
301 sub_divide(aLine, startT, endT, dst);
302 sub[0].fX = SkDoubleToScalar(dst[0].x);
303 sub[0].fY = SkDoubleToScalar(dst[0].y);
304 sub[1].fX = SkDoubleToScalar(dst[1].x);
305 sub[1].fY = SkDoubleToScalar(dst[1].y);
306}
307
308static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
309 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000310 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000311 Quadratic dst;
312 sub_divide(aQuad, startT, endT, dst);
313 sub[0].fX = SkDoubleToScalar(dst[0].x);
314 sub[0].fY = SkDoubleToScalar(dst[0].y);
315 sub[1].fX = SkDoubleToScalar(dst[1].x);
316 sub[1].fY = SkDoubleToScalar(dst[1].y);
317 sub[2].fX = SkDoubleToScalar(dst[2].x);
318 sub[2].fY = SkDoubleToScalar(dst[2].y);
319}
320
321static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
322 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000323 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000324 Cubic dst;
325 sub_divide(aCubic, startT, endT, dst);
326 sub[0].fX = SkDoubleToScalar(dst[0].x);
327 sub[0].fY = SkDoubleToScalar(dst[0].y);
328 sub[1].fX = SkDoubleToScalar(dst[1].x);
329 sub[1].fY = SkDoubleToScalar(dst[1].y);
330 sub[2].fX = SkDoubleToScalar(dst[2].x);
331 sub[2].fY = SkDoubleToScalar(dst[2].y);
332 sub[3].fX = SkDoubleToScalar(dst[3].x);
333 sub[3].fY = SkDoubleToScalar(dst[3].y);
334}
335
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000336static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
337 SkPoint []) = {
338 NULL,
339 LineSubDivide,
340 QuadSubDivide,
341 CubicSubDivide
342};
343
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000344static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000345 _Line sub) {
346 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000347 _Line dst;
348 sub_divide(aLine, startT, endT, dst);
349 sub[0] = dst[0];
350 sub[1] = dst[1];
351}
352
353static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000354 Quadratic sub) {
355 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000356 Quadratic dst;
357 sub_divide(aQuad, startT, endT, dst);
358 sub[0] = dst[0];
359 sub[1] = dst[1];
360 sub[2] = dst[2];
361}
362
363static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000364 Cubic sub) {
365 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000366 Cubic dst;
367 sub_divide(aCubic, startT, endT, dst);
368 sub[0] = dst[0];
369 sub[1] = dst[1];
370 sub[2] = dst[2];
371 sub[3] = dst[3];
372}
373
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000374#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000375static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
376 SkRect& bounds) {
377 SkPoint dst[3];
378 QuadSubDivide(a, startT, endT, dst);
379 bounds.fLeft = bounds.fRight = dst[0].fX;
380 bounds.fTop = bounds.fBottom = dst[0].fY;
381 for (int index = 1; index < 3; ++index) {
382 bounds.growToInclude(dst[index].fX, dst[index].fY);
383 }
384}
385
386static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
387 SkRect& bounds) {
388 SkPoint dst[4];
389 CubicSubDivide(a, startT, endT, dst);
390 bounds.fLeft = bounds.fRight = dst[0].fX;
391 bounds.fTop = bounds.fBottom = dst[0].fY;
392 for (int index = 1; index < 4; ++index) {
393 bounds.growToInclude(dst[index].fX, dst[index].fY);
394 }
395}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000396#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000397
caryclark@google.com15fa1382012-05-07 20:49:36 +0000398static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000399 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000400 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000401 Quadratic dst;
402 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000403 if (order == 2) { // quad became line
404 for (int index = 0; index < order; ++index) {
405 SkPoint* pt = reducePts.append();
406 pt->fX = SkDoubleToScalar(dst[index].x);
407 pt->fY = SkDoubleToScalar(dst[index].y);
408 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000409 }
410 return (SkPath::Verb) (order - 1);
411}
412
413static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
414 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000415 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000416 Cubic dst;
417 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000418 if (order == 2 || order == 3) { // cubic became line or quad
419 for (int index = 0; index < order; ++index) {
420 SkPoint* pt = reducePts.append();
421 pt->fX = SkDoubleToScalar(dst[index].x);
422 pt->fY = SkDoubleToScalar(dst[index].y);
423 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000424 }
425 return (SkPath::Verb) (order - 1);
426}
427
caryclark@google.com15fa1382012-05-07 20:49:36 +0000428static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000429 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000430 return isLinear(aQuad, 0, 2);
431}
432
433static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000434 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000435 return isLinear(aCubic, 0, 3);
436}
437
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000438static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000439 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000440 double x[2];
441 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000442 xy_at_t(aLine, endT, x[1], *(double*) 0);
443 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000444}
445
446static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000447 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000448 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000449}
450
451static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000452 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000453 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000454}
455
456static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
457 NULL,
458 LineLeftMost,
459 QuadLeftMost,
460 CubicLeftMost
461};
462
caryclark@google.com235f56a2012-09-14 14:19:30 +0000463static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
464 Intersections& intersections) {
465 MAKE_CONST_QUAD(aQuad, a);
466 MAKE_CONST_LINE(bLine, b);
467 return intersectRay(aQuad, bLine, intersections);
468}
469
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000470class Segment;
471
caryclark@google.com15fa1382012-05-07 20:49:36 +0000472// sorting angles
473// given angles of {dx dy ddx ddy dddx dddy} sort them
474class Angle {
475public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000476 // FIXME: this is bogus for quads and cubics
477 // if the quads and cubics' line from end pt to ctrl pt are coincident,
478 // there's no obvious way to determine the curve ordering from the
479 // derivatives alone. In particular, if one quadratic's coincident tangent
480 // is longer than the other curve, the final control point can place the
481 // longer curve on either side of the shorter one.
482 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
483 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000484
caryclark@google.com32546db2012-08-31 20:55:07 +0000485 // start here
486 /*(
487 for quads and cubics, set up a parameterized line (e.g. LineParameters )
488 for points [0] to [1]. See if point [2] is on that line, or on one side
489 or the other. If it both quads' end points are on the same side, choose
490 the shorter tangent. If the tangents are equal, choose the better second
491 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000492
caryclark@google.com32546db2012-08-31 20:55:07 +0000493 maybe I set up LineParameters lazily
494 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000495 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000496 double y = dy();
497 double ry = rh.dy();
498 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
499 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000500 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000501 double x = dx();
502 double rx = rh.dx();
503 if (y == 0 && ry == 0 && x * rx < 0) {
504 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000505 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000506 AngleValue x_ry = x * ry;
507 AngleValue rx_y = rx * y;
508 AngleValue cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000509 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000510 return cmp < 0;
511 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000512 if (approximately_zero(x_ry) && approximately_zero(rx_y)
513 && !approximately_zero_squared(cmp)) {
514 return cmp < 0;
515 }
caryclark@google.com32546db2012-08-31 20:55:07 +0000516 // at this point, the initial tangent line is coincident
517 #if !HIGH_DEF_ANGLES // old way
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000518 AngleValue dy = approximately_pin(fDy + fDDy);
519 AngleValue rdy = approximately_pin(rh.fDy + rh.fDDy);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000520 if (dy * rdy < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000521 return dy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000522 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000523 AngleValue dx = approximately_pin(fDx + fDDx);
524 AngleValue rdx = approximately_pin(rh.fDx + rh.fDDx);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000525 if (dy == 0 && rdy == 0 && dx * rdx < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000526 return dx < rdx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000527 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000528 cmp = dx * rdy - rdx * dy;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000529 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000530 return cmp < 0;
531 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000532 dy = approximately_pin(dy + fDDDy);
533 rdy = approximately_pin(rdy + rh.fDDDy);
534 if (dy * rdy < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000535 return dy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000536 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000537 dx = approximately_pin(dx + fDDDx);
538 rdx = approximately_pin(rdx + rh.fDDDx);
539 if (dy == 0 && rdy == 0 && dx * rdx < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000540 return dx < rdx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000541 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000542 return dx * rdy < rdx * dy;
caryclark@google.com32546db2012-08-31 20:55:07 +0000543 #else // new way
544 if (fSide * rh.fSide <= 0) {
caryclark@google.comd1688742012-09-18 20:08:37 +0000545 // FIXME: running demo will trigger this assertion
546 // (don't know if commenting out will trigger further assertion or not)
547 // commenting it out allows demo to run in release, though
caryclark@google.com32546db2012-08-31 20:55:07 +0000548 SkASSERT(fSide != rh.fSide);
549 return fSide < rh.fSide;
550 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000551 #if 0 // the following code is a bust. In particular, tangent length doesn't provide a sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000552 if (y != ry) {
553 return (fabs(y) < fabs(ry)) ^ (fSide > 0);
caryclark@google.com32546db2012-08-31 20:55:07 +0000554 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000555 if (x != rx) {
556 return (fabs(x) < fabs(rx)) ^ (fSide > 0);
caryclark@google.com32546db2012-08-31 20:55:07 +0000557 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000558 // sort by second tangent angle
559 cmp = fD2x * rh.fD2y - rh.fD2x * fD2y;
560 if (!approximately_zero(cmp)) {
561 return cmp < 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000562 }
563 SkASSERT(0); // add code for cubic case
caryclark@google.com235f56a2012-09-14 14:19:30 +0000564 #else
565 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
566 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000567 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000568 // end of the shorter tangent to midway between the end points
569 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000570 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
571 double len = fTangent1.normalSquared();
572 double rlen = rh.fTangent1.normalSquared();
573 SkPoint ray[2];
caryclark@google.com235f56a2012-09-14 14:19:30 +0000574 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000575 int roots, rroots;
576 bool flip = false;
577 do {
578 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
579 double midX = (q[0].x + q[2].x) / 2;
580 double midY = (q[0].y + q[2].y) / 2;
581 // FIXME: Ugh! this feels like a huge hack, not sure what else to do
582 while (approximately_equal(q[1].x, midX) && approximately_equal(q[1].y, midY)) {
583 SkASSERT(midX - q[1].x || midY - q[1].y);
584 midX += midX - q[1].x;
585 midY += midY - q[1].y;
586 }
587 ray[0].fX = SkDoubleToScalar(q[1].x);
588 ray[0].fY = SkDoubleToScalar(q[1].y);
589 ray[1].fX = SkDoubleToScalar(midX);
590 ray[1].fY = SkDoubleToScalar(midY);
591 SkASSERT(ray[0] != ray[1]);
592 roots = QuadRayIntersect(fPts, ray, i);
593 rroots = QuadRayIntersect(rh.fPts, ray, ri);
594 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.com235f56a2012-09-14 14:19:30 +0000595 SkASSERT(roots > 0);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000596 SkASSERT(rroots > 0);
597 SkPoint loc;
598 SkScalar best = SK_ScalarInfinity;
599 SkScalar dx, dy, dist;
600 int index;
601 for (index = 0; index < roots; ++index) {
602 QuadXYAtT(fPts, i.fT[0][index], &loc);
603 dx = loc.fX - ray[0].fX;
604 dy = loc.fY - ray[0].fY;
605 dist = dx * dx + dy * dy;
606 if (best > dist) {
607 best = dist;
608 }
609 }
610 for (index = 0; index < rroots; ++index) {
611 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
612 dx = loc.fX - ray[0].fX;
613 dy = loc.fY - ray[0].fY;
614 dist = dx * dx + dy * dy;
615 if (best > dist) {
616 return fSide < 0;
617 }
618 }
619 return fSide > 0;
620 #endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000621 #endif
caryclark@google.com15fa1382012-05-07 20:49:36 +0000622 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000623
caryclark@google.com47580692012-07-23 12:14:49 +0000624 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000625#if HIGH_DEF_ANGLES
626 return fTangent1.dx();
627#else
caryclark@google.com47580692012-07-23 12:14:49 +0000628 return fDx;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000629#endif
caryclark@google.com47580692012-07-23 12:14:49 +0000630 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000631
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000632 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000633#if HIGH_DEF_ANGLES
634 return fTangent1.dy();
635#else
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000636 return fDy;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000637#endif
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000638 }
639
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000640 int end() const {
641 return fEnd;
642 }
643
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000644 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000645#if HIGH_DEF_ANGLES
646 return dy() == 0 && fVerb == SkPath::kLine_Verb;
647#else
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000648 return fDy == 0 && fDDy == 0 && fDDDy == 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000649#endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000650 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000651
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000652 // high precision version
653#if HIGH_DEF_ANGLES
654 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
655 int start, int end, double startT, double endT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000656 fSegment = segment;
657 fStart = start;
658 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000659 fPts = orig;
660 fVerb = verb;
caryclark@google.com32546db2012-08-31 20:55:07 +0000661 switch (verb) {
662 case SkPath::kLine_Verb:
663 _Line l;
664 LineSubDivideHD(orig, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000665 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
666 fTangent1.lineEndPoints(l);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000667 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000668 break;
669 case SkPath::kQuad_Verb:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000670 QuadSubDivideHD(orig, startT, endT, fQ);
671 fTangent1.quadEndPoints(fQ, 0, 1);
672 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000673 break;
674 case SkPath::kCubic_Verb:
675 Cubic c;
676 CubicSubDivideHD(orig, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000677 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000678 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000679 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000680 default:
681 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000682 }
caryclark@google.com32546db2012-08-31 20:55:07 +0000683 // OPTIMIZATION: don't need these, access fTangent1 directly
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000684 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000685
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000686#else
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000687 // since all angles share a point, this needs to know which point
688 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
689 // practically, this should only be called by addAngle
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000690 void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000691 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000692 SkASSERT(start != end);
693 fSegment = segment;
694 fStart = start;
695 fEnd = end;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000696 fDx = approximately_pin(pts[1].fX - pts[0].fX); // b - a
697 fDy = approximately_pin(pts[1].fY - pts[0].fY);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000698 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000699 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000700 return;
701 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000702 fDDx = approximately_pin(pts[2].fX - pts[1].fX - fDx); // a - 2b + c
703 fDDy = approximately_pin(pts[2].fY - pts[1].fY - fDy);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000704 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000705 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000706 return;
707 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000708 fDDDx = approximately_pin(pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX);
709 fDDDy = approximately_pin(pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000710 }
711
712 // noncoincident quads/cubics may have the same initial angle
713 // as lines, so must sort by derivatives as well
714 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000715 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000716 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000717 fSegment = segment;
718 fStart = start;
719 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000720 fDx = pts[1].fX - pts[0].fX; // b - a
721 fDy = pts[1].fY - pts[0].fY;
722 if (verb == SkPath::kLine_Verb) {
723 fDDx = fDDy = fDDDx = fDDDy = 0;
724 return;
725 }
726 if (verb == SkPath::kQuad_Verb) {
727 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
728 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
729 int larger = std::max(abs(uplsX), abs(uplsY));
730 int shift = 0;
731 double flatT;
732 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
733 LineParameters implicitLine;
caryclark@google.com32546db2012-08-31 20:55:07 +0000734 MAKE_CONST_LINE(tangent, pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000735 implicitLine.lineEndPoints(tangent);
736 implicitLine.normalize();
737 while (larger > UlpsEpsilon * 1024) {
738 larger >>= 2;
739 ++shift;
740 flatT = 0.5 / (1 << shift);
741 QuadXYAtT(pts, flatT, &ddPt);
742 _Point _pt = {ddPt.fX, ddPt.fY};
743 double distance = implicitLine.pointDistance(_pt);
744 if (approximately_zero(distance)) {
745 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
746 break;
747 }
748 }
749 flatT = 0.5 / (1 << shift);
750 QuadXYAtT(pts, flatT, &ddPt);
751 fDDx = ddPt.fX - pts[0].fX;
752 fDDy = ddPt.fY - pts[0].fY;
753 SkASSERT(fDDx != 0 || fDDy != 0);
754 fDDDx = fDDDy = 0;
755 return;
756 }
757 SkASSERT(0); // FIXME: add cubic case
758 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000759#endif
rmistry@google.comd6176b02012-08-23 18:14:13 +0000760
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000761 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000762 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000763 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000764
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000765 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000766 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000767 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000768
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000769 int start() const {
770 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000771 }
772
caryclark@google.comc899ad92012-08-23 15:24:42 +0000773#if DEBUG_ANGLE
774 void debugShow(const SkPoint& a) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000775#if HIGH_DEF_ANGLES
776 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n",
777 dx(), dy(), fSide);
778#else
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000779 SkDebugf(" d=(%1.9g,%1.9g) dd=(%1.9g,%1.9g) ddd=(%1.9g,%1.9g)\n",
caryclark@google.comc899ad92012-08-23 15:24:42 +0000780 fDx, fDy, fDDx, fDDy, fDDDx, fDDDy);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000781 AngleValue ax = (AngleValue) a.fX;
782 AngleValue ay = (AngleValue) a.fY;
783 AngleValue bx, by, cx, cy, dx, dy;
784 bx = ax + fDx; // add b - a
785 by = ay + fDy;
786 cx = ax + 2 * fDx + fDDx; // add a + 2(b - a) to a - 2b + c
787 cy = ay + 2 * fDy + fDDy;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000788 if (fDDDx == 0 && fDDDy == 0) {
789 if (fDDx == 0 && fDDy == 0) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000790 SkDebugf(
791" {SkPath::kLine_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g} }},\n",
792 ax, ay, bx, by);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000793 } else {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000794 SkDebugf(
795" {SkPath::kQuad_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}}},\n",
796 ax, ay, bx, by, cx, cy);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000797 }
798 } else {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000799 dx = fDDDx - ax - 3 * (cx - bx);
800 dy = fDDDy - ay - 3 * (cy - by);
801 SkDebugf(
802" {SkPath::kCubic_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}}},\n",
803 ax, ay, bx, by, cx, cy, dx, dy);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000804 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000805#endif
caryclark@google.comc899ad92012-08-23 15:24:42 +0000806 }
807#endif
808
caryclark@google.com15fa1382012-05-07 20:49:36 +0000809private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000810#if HIGH_DEF_ANGLES
811 const SkPoint* fPts;
812 Quadratic fQ;
813 SkPath::Verb fVerb;
814 double fSide;
815 LineParameters fTangent1;
816#else
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000817 AngleValue fDx;
818 AngleValue fDy;
819 AngleValue fDDx;
820 AngleValue fDDy;
821 AngleValue fDDDx;
822 AngleValue fDDDy;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000823#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000824 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000825 int fStart;
826 int fEnd;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000827};
828
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000829static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
830 int angleCount = angles.count();
831 int angleIndex;
832 angleList.setReserve(angleCount);
833 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
834 *angleList.append() = &angles[angleIndex];
835 }
836 QSort<Angle>(angleList.begin(), angleList.end() - 1);
837}
838
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000839// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000840struct Bounds : public SkRect {
841 static bool Intersects(const Bounds& a, const Bounds& b) {
842 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
843 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
844 }
845
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000846 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
847 if (left < fLeft) {
848 fLeft = left;
849 }
850 if (top < fTop) {
851 fTop = top;
852 }
853 if (right > fRight) {
854 fRight = right;
855 }
856 if (bottom > fBottom) {
857 fBottom = bottom;
858 }
859 }
860
861 void add(const Bounds& toAdd) {
862 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
863 }
864
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000865 bool isEmpty() {
866 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000867 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000868 || isnan(fLeft) || isnan(fRight)
869 || isnan(fTop) || isnan(fBottom);
870 }
871
872 void setCubicBounds(const SkPoint a[4]) {
873 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000874 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000875 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000876 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
877 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000878 }
879
880 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000881 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000882 _Rect dRect;
883 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000884 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
885 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000886 }
887};
888
caryclark@google.com2ddff932012-08-07 21:25:27 +0000889static bool useInnerWinding(int outerWinding, int innerWinding) {
890 SkASSERT(outerWinding != innerWinding);
891 int absOut = abs(outerWinding);
892 int absIn = abs(innerWinding);
893 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
894 if (outerWinding * innerWinding < 0) {
895#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000896 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000897 outerWinding, innerWinding, result ? "true" : "false");
898#endif
899 }
900 return result;
901}
902
caryclark@google.com15fa1382012-05-07 20:49:36 +0000903struct Span {
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000904 Segment* fOther;
caryclark@google.com27c449a2012-07-27 18:26:38 +0000905 mutable SkPoint fPt; // lazily computed as needed
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000906 double fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000907 double fOtherT; // value at fOther[fOtherIndex].fT
908 int fOtherIndex; // can't be used during intersection
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000909 int fWindSum; // accumulated from contours surrounding this one
910 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com235f56a2012-09-14 14:19:30 +0000911 int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000912 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000913};
914
caryclark@google.com235f56a2012-09-14 14:19:30 +0000915static const bool opLookup[][2][2] = {
916 // ==0 !=0
917 // b a b a
918 {{true , false}, {false, true }}, // a - b
919 {{false, false}, {true , true }}, // a & b
920 {{true , true }, {false, false}}, // a | b
921 {{true , true }, {true , true }}, // a ^ b
922};
923
924static bool activeOp(bool angleIsOp, int otherNonZero, ShapeOp op) {
925 return opLookup[op][otherNonZero][angleIsOp];
926}
927
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000928class Segment {
929public:
930 Segment() {
931#if DEBUG_DUMP
932 fID = ++gSegmentID;
933#endif
934 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000935
caryclark@google.com9764cc62012-07-12 19:29:45 +0000936 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
937 if (activeAngleInner(index, done, angles)) {
938 return true;
939 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000940 double referenceT = fTs[index].fT;
941 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000942 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000943 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000944 return true;
945 }
946 }
947 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000948 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000949 return true;
950 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000951 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000952 return false;
953 }
954
caryclark@google.com9764cc62012-07-12 19:29:45 +0000955 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000956 Span* span = &fTs[index];
957 Segment* other = span->fOther;
958 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +0000959 return other->activeAngleInner(oIndex, done, angles);
960 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000961
caryclark@google.com9764cc62012-07-12 19:29:45 +0000962 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
963 int next = nextSpan(index, 1);
964 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000965 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000966 if (upSpan.fWindValue) {
967 addAngle(angles, index, next);
968 if (upSpan.fDone) {
969 done++;
970 } else if (upSpan.fWindSum != SK_MinS32) {
971 return true;
972 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000973 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000974 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000975 int prev = nextSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000976 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +0000977 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000978 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000979 if (downSpan.fWindValue) {
980 addAngle(angles, index, prev);
981 if (downSpan.fDone) {
982 done++;
983 } else if (downSpan.fWindSum != SK_MinS32) {
984 return true;
985 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000986 }
987 }
988 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000989 }
990
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000991 SkScalar activeTop() const {
992 SkASSERT(!done());
993 int count = fTs.count();
994 SkScalar result = SK_ScalarMax;
995 bool lastDone = true;
996 for (int index = 0; index < count; ++index) {
997 bool done = fTs[index].fDone;
998 if (!done || !lastDone) {
999 SkScalar y = yAtT(index);
1000 if (result > y) {
1001 result = y;
1002 }
1003 }
1004 lastDone = done;
1005 }
1006 SkASSERT(result < SK_ScalarMax);
1007 return result;
1008 }
1009
1010 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001011 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001012 Angle* angle = angles.append();
1013#if HIGH_DEF_ANGLES==0 // old way
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001014 SkPoint edge[4];
1015 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001016 angle->set(edge, fVerb, this, start, end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001017#else // new way : compute temp edge in higher precision
1018 angle->set(fPts, fVerb, this, start, end, fTs[start].fT, fTs[end].fT);
1019#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001020 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001021
caryclark@google.com2ddff932012-08-07 21:25:27 +00001022 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001023 double oEnd) {
1024 int tIndex = -1;
1025 int tCount = fTs.count();
1026 int oIndex = -1;
1027 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001028 do {
1029 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001030 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001031 int tIndexStart = tIndex;
1032 do {
1033 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001034 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001035 int oIndexStart = oIndex;
1036 double nextT;
1037 do {
1038 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001039 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001040 double oNextT;
1041 do {
1042 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001043 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001044 // at this point, spans before and after are at:
1045 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1046 // if tIndexStart == 0, no prior span
1047 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001048
caryclark@google.comcc905052012-07-25 20:59:42 +00001049 // advance the span with zero winding
1050 // if the following span exists (not past the end, non-zero winding)
1051 // connect the two edges
1052 if (!fTs[tIndexStart].fWindValue) {
1053 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1054 #if DEBUG_CONCIDENT
1055 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1056 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001057 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1058 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001059 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001060 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001061 }
1062 if (nextT < 1 && fTs[tIndex].fWindValue) {
1063 #if DEBUG_CONCIDENT
1064 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1065 __FUNCTION__, fID, other.fID, tIndex,
1066 fTs[tIndex].fT, xyAtT(tIndex).fX,
1067 xyAtT(tIndex).fY);
1068 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001069 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001070 }
1071 } else {
1072 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1073 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1074 #if DEBUG_CONCIDENT
1075 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1076 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001077 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1078 other.xyAtT(oIndexStart).fY);
1079 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001080 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001081 }
1082 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1083 #if DEBUG_CONCIDENT
1084 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1085 __FUNCTION__, fID, other.fID, oIndex,
1086 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1087 other.xyAtT(oIndex).fY);
1088 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1089 #endif
1090 }
1091 }
1092 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001093
caryclark@google.comcc905052012-07-25 20:59:42 +00001094 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1095 double oEnd) {
1096 // walk this to outsideTs[0]
1097 // walk other to outsideTs[1]
1098 // if either is > 0, add a pointer to the other, copying adjacent winding
1099 int tIndex = -1;
1100 int oIndex = -1;
1101 double tStart = outsideTs[0];
1102 double oStart = outsideTs[1];
1103 do {
1104 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001105 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001106 do {
1107 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001108 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001109 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001110 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001111 }
1112 tStart = fTs[tIndex].fT;
1113 oStart = other.fTs[oIndex].fT;
1114 do {
1115 double nextT;
1116 do {
1117 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001118 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001119 tStart = nextT;
1120 do {
1121 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001122 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001123 oStart = nextT;
1124 if (tStart == 1 && oStart == 1) {
1125 break;
1126 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001127 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001128 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001129 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001130
caryclark@google.com235f56a2012-09-14 14:19:30 +00001131 void addCubic(const SkPoint pts[4], bool operand) {
1132 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001133 fBounds.setCubicBounds(pts);
1134 }
1135
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001136 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001137 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001138 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001139 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001140 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001141 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001142 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001143 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
1144 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
1145 if (fVerb > 1) {
1146 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
1147 }
1148 if (fVerb > 2) {
1149 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
1150 }
1151 SkDebugf("\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001152 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001153 switch (fVerb) {
1154 case SkPath::kLine_Verb:
1155 path.lineTo(edge[1].fX, edge[1].fY);
1156 break;
1157 case SkPath::kQuad_Verb:
1158 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
1159 break;
1160 case SkPath::kCubic_Verb:
1161 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
1162 edge[3].fX, edge[3].fY);
1163 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001164 default:
1165 SkASSERT(0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001166 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001167 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001168 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001169 }
1170
caryclark@google.com235f56a2012-09-14 14:19:30 +00001171 void addLine(const SkPoint pts[2], bool operand) {
1172 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001173 fBounds.set(pts, 2);
1174 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001175
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001176 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
1177 const SkPoint& pt = xyAtT(tIndex);
1178 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001179 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001180 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001181 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001182 path.moveTo(pt.fX, pt.fY);
1183 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001184 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001185 }
1186
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001187 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001188 void addOtherT(int index, double otherT, int otherIndex) {
1189 Span& span = fTs[index];
1190 span.fOtherT = otherT;
1191 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001192 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001193
caryclark@google.com235f56a2012-09-14 14:19:30 +00001194 void addQuad(const SkPoint pts[3], bool operand) {
1195 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001196 fBounds.setQuadBounds(pts);
1197 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001198
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001199 // Defer all coincident edge processing until
1200 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001201
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001202// no need to be tricky; insert in normal T order
1203// resolve overlapping ts when considering coincidence later
1204
1205 // add non-coincident intersection. Resulting edges are sorted in T.
1206 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001207 // FIXME: in the pathological case where there is a ton of intercepts,
1208 // binary search?
1209 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001210 size_t tCount = fTs.count();
caryclark@google.comc899ad92012-08-23 15:24:42 +00001211 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001212 if (approximately_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001213 newT = 0;
1214 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001215 if (approximately_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001216 newT = 1;
1217 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001218 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001219 // OPTIMIZATION: if there are three or more identical Ts, then
1220 // the fourth and following could be further insertion-sorted so
1221 // that all the edges are clockwise or counterclockwise.
1222 // This could later limit segment tests to the two adjacent
1223 // neighbors, although it doesn't help with determining which
1224 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001225 if (newT < fTs[index].fT) {
1226 insertedAt = index;
1227 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001228 }
1229 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001230 Span* span;
1231 if (insertedAt >= 0) {
1232 span = fTs.insert(insertedAt);
1233 } else {
1234 insertedAt = tCount;
1235 span = fTs.append();
1236 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001237 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001238 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001239 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001240 span->fWindSum = SK_MinS32;
1241 span->fWindValue = 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001242 span->fWindValueOpp = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001243 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001244 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001245 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001246 return insertedAt;
1247 }
1248
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001249 // set spans from start to end to decrement by one
1250 // note this walks other backwards
1251 // FIMXE: there's probably an edge case that can be constructed where
1252 // two span in one segment are separated by float epsilon on one span but
1253 // not the other, if one segment is very small. For this
1254 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001255 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001256 // sorted? It feels better in such a case to match the span's other span
1257 // pointer since both coincident segments must contain the same spans.
1258 void addTCancel(double startT, double endT, Segment& other,
1259 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001260 SkASSERT(!approximately_negative(endT - startT));
1261 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001262 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001263 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001264 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001265 ++index;
1266 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001267 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001268 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001269 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001270 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001271 Span* test = &fTs[index];
1272 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001273 SkTDArray<double> outsideTs;
1274 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001275 do {
1276 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001277 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001278 double testT = test->fT;
1279 double oTestT = oTest->fT;
1280 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001281 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001282 if (decrement) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001283 if (binary) {
1284 --(span->fWindValueOpp);
1285 } else {
1286 decrementSpan(span);
1287 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001288 } else if (track && span->fT < 1 && oTestT < 1) {
1289 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001290 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001291 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001292 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001293 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001294 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1295 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1296 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001297 while (approximately_negative(otherTMatchStart - oSpan->fT)
1298 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001299 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001300 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001301 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001302 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001303 other.decrementSpan(oSpan);
1304 } else if (track && oSpan->fT < 1 && testT < 1) {
1305 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001306 }
1307 if (!oIndex) {
1308 break;
1309 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001310 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001311 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001312 test = span;
1313 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001314 } while (!approximately_negative(endT - test->fT));
1315 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001316 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001317 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001318 double tStart = outsideTs[0];
1319 double oStart = outsideTs[1];
1320 addCancelOutsides(tStart, oStart, other, oEndT);
1321 int count = outsideTs.count();
1322 if (count > 2) {
1323 double tStart = outsideTs[count - 2];
1324 double oStart = outsideTs[count - 1];
1325 addCancelOutsides(tStart, oStart, other, oEndT);
1326 }
caryclark@google.com18063442012-07-25 12:05:18 +00001327 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001328 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001329 double tStart = oOutsideTs[0];
1330 double oStart = oOutsideTs[1];
1331 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001332 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001333 }
1334
1335 // set spans from start to end to increment the greater by one and decrement
1336 // the lesser
caryclark@google.com235f56a2012-09-14 14:19:30 +00001337 void addTCoincident(const bool isXor, double startT, double endT,
1338 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001339 SkASSERT(!approximately_negative(endT - startT));
1340 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001341 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001342 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001343 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001344 ++index;
1345 }
1346 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001347 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001348 ++oIndex;
1349 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001350 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001351 Span* test = &fTs[index];
1352 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001353 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001354 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001355 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001356 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001357 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001358 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001359 bool decrementThis = (test->fWindValue < oTest->fWindValue) & !isXor;
1360 bool decrementOther = (test->fWindValue >= oTest->fWindValue) & !isXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001361 Span* end = test;
1362 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001363 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001364 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001365 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001366 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001367 if (transfer) {
1368 if (decrementOther) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001369 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001370 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001371 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001372 if (binary) {
1373 ++(end->fWindValueOpp);
1374 } else {
1375 ++(end->fWindValue);
1376 }
caryclark@google.com18063442012-07-25 12:05:18 +00001377 } else if (decrementSpan(end)) {
1378 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001379 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001380 } else if (oTest->fWindValue) {
1381 SkASSERT(!decrementOther);
1382 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1383 TrackOutside(xOutsideTs, end->fT, oStartT);
1384 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001385 }
1386 end = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001387 } while (approximately_negative(end->fT - test->fT));
caryclark@google.com59823f72012-08-09 18:17:47 +00001388 // because of the order in which coincidences are resolved, this and other
1389 // may not have the same intermediate points. Compute the corresponding
1390 // intermediate T values (using this as the master, other as the follower)
1391 // and walk other conditionally -- hoping that it catches up in the end
1392 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001393 Span* oEnd = oTest;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001394 while (!approximately_negative(oEndT - oEnd->fT) && approximately_negative(oEnd->fT - otherTMatch)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00001395 if (transfer) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001396 if (decrementThis) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001397 #ifdef SK_DEBUG
1398 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1399 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001400 if (binary) {
1401 ++(oEnd->fWindValueOpp);
1402 } else {
1403 ++(oEnd->fWindValue);
1404 }
caryclark@google.com18063442012-07-25 12:05:18 +00001405 } else if (other.decrementSpan(oEnd)) {
1406 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001407 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001408 } else if (test->fWindValue) {
1409 SkASSERT(!decrementOther);
1410 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1411 SkASSERT(0); // track for later?
1412 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001413 }
1414 oEnd = &other.fTs[++oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001415 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001416 test = end;
1417 oTest = oEnd;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001418 } while (!approximately_negative(endT - test->fT));
1419 SkASSERT(approximately_negative(oTest->fT - oEndT));
1420 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001421 if (!done()) {
1422 if (outsideTs.count()) {
1423 addCoinOutsides(outsideTs, other, oEndT);
1424 }
1425 if (xOutsideTs.count()) {
1426 addCoinOutsides(xOutsideTs, other, oEndT);
1427 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001428 }
1429 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001430 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001431 }
1432 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001433
caryclark@google.comcc905052012-07-25 20:59:42 +00001434 // FIXME: this doesn't prevent the same span from being added twice
1435 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001436 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001437 int tCount = fTs.count();
1438 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1439 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001440 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001441 break;
1442 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001443 if (approximately_negative(span.fT - t) && span.fOther == &other && span.fOtherT == otherT) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001444#if DEBUG_ADD_T_PAIR
1445 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1446 __FUNCTION__, fID, t, other.fID, otherT);
1447#endif
1448 return;
1449 }
1450 }
caryclark@google.com47580692012-07-23 12:14:49 +00001451#if DEBUG_ADD_T_PAIR
1452 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1453 __FUNCTION__, fID, t, other.fID, otherT);
1454#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001455 int insertedAt = addT(t, &other);
1456 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001457 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001458 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001459 matchWindingValue(insertedAt, t, borrowWind);
1460 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001461 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001462
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001463 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001464 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001465 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1466 addAngle(angles, end, start);
1467 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001468 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001469 int step = SkSign32(end - start);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001470 int tIndex = nextSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001471 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001472 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001473 }
1474 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001475
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001476 const Bounds& bounds() const {
1477 return fBounds;
1478 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001479
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001480 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001481 double referenceT = fTs[index].fT;
1482 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001483 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001484 buildAnglesInner(lesser, angles);
1485 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001486 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001487 buildAnglesInner(index, angles);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001488 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001489 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001490
1491 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1492 Span* span = &fTs[index];
1493 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001494 // if there is only one live crossing, and no coincidence, continue
1495 // in the same direction
1496 // if there is coincidence, the only choice may be to reverse direction
1497 // find edge on either side of intersection
1498 int oIndex = span->fOtherIndex;
1499 // if done == -1, prior span has already been processed
1500 int step = 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001501 int next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001502 if (next < 0) {
1503 step = -step;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001504 next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001505 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001506 // add candidate into and away from junction
1507 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001508 }
1509
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001510 // figure out if the segment's ascending T goes clockwise or not
1511 // not enough context to write this as shown
1512 // instead, add all segments meeting at the top
1513 // sort them using buildAngleList
1514 // find the first in the sort
1515 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001516 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001517 SkASSERT(0); // incomplete
1518 return false;
1519 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001520
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001521 int computeSum(int startIndex, int endIndex) {
1522 SkTDArray<Angle> angles;
1523 addTwoAngles(startIndex, endIndex, angles);
1524 buildAngles(endIndex, angles);
caryclark@google.comd1688742012-09-18 20:08:37 +00001525 // OPTIMIZATION: check all angles to see if any have computed wind sum
1526 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001527 SkTDArray<Angle*> sorted;
1528 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001529#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00001530 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001531#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001532 int angleCount = angles.count();
1533 const Angle* angle;
1534 const Segment* base;
1535 int winding;
1536 int firstIndex = 0;
1537 do {
1538 angle = sorted[firstIndex];
1539 base = angle->segment();
1540 winding = base->windSum(angle);
1541 if (winding != SK_MinS32) {
1542 break;
1543 }
1544 if (++firstIndex == angleCount) {
1545 return SK_MinS32;
1546 }
1547 } while (true);
1548 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001549 int spanWinding = base->spanSign(angle);
1550 bool inner = useInnerWinding(winding + spanWinding, winding);
1551 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001552 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001553 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001554 inner ? winding + spanWinding : winding);
1555 #endif
1556 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001557 winding += spanWinding;
1558 }
1559 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001560 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001561 #endif
1562 int nextIndex = firstIndex + 1;
1563 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001564 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001565 do {
1566 if (nextIndex == angleCount) {
1567 nextIndex = 0;
1568 }
1569 angle = sorted[nextIndex];
1570 Segment* segment = angle->segment();
1571 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001572 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001573 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001574 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001575 maxWinding = winding;
1576 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001577 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001578 }
1579 } while (++nextIndex != lastIndex);
1580 return windSum(SkMin32(startIndex, endIndex));
1581 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001582
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001583 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001584 int bestT = -1;
1585 SkScalar top = bounds().fTop;
1586 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001587 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001588 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001589 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001590 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001591 if (fTs[start].fWindValue == 0) {
1592 continue;
1593 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001594 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001595 double startT = fTs[start].fT;
1596 double endT = fTs[end].fT;
1597 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001598 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001599 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001600 // FIXME: always use original and limit results to T values within
1601 // start t and end t.
1602 // OPTIMIZE: use specialty function that intersects ray with curve,
1603 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001604 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1605 false, intersections);
1606 if (pts == 0) {
1607 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001608 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001609 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1610 // if the intersection is edge on, wait for another one
1611 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001612 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001613 for (int index = 0; index < pts; ++index) {
1614 SkPoint pt;
1615 double foundT = intersections.fT[0][index];
1616 double testT = startT + (endT - startT) * foundT;
1617 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1618 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001619 if (fVerb > SkPath::kLine_Verb
1620 && !approximately_less_than_zero(foundT)
1621 && !approximately_greater_than_one(foundT)) {
1622 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1623 if (approximately_zero(dx)) {
1624 continue;
1625 }
1626 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001627 bestY = pt.fY;
1628 bestT = foundT < 1 ? start : end;
1629 hitT = testT;
1630 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001631 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001632 } while (fTs[end].fT != 1);
1633 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001634 }
caryclark@google.com18063442012-07-25 12:05:18 +00001635
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001636 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1637 // if a segment is connected to this one, consider it crossing
1638 int tIndex;
1639 if (fPts[0].fX == basePt.fX) {
1640 tIndex = 0;
1641 do {
1642 const Span& sSpan = fTs[tIndex];
1643 const Segment* sOther = sSpan.fOther;
1644 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1645 continue;
1646 }
1647 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1648 : sOther->fBounds.fRight > basePt.fX) {
1649 return true;
1650 }
1651 } while (fTs[++tIndex].fT == 0);
1652 }
1653 if (fPts[fVerb].fX == basePt.fX) {
1654 tIndex = fTs.count() - 1;
1655 do {
1656 const Span& eSpan = fTs[tIndex];
1657 const Segment* eOther = eSpan.fOther;
1658 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1659 continue;
1660 }
1661 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1662 : eOther->fBounds.fRight > basePt.fX) {
1663 return true;
1664 }
1665 } while (fTs[--tIndex].fT == 1);
1666 }
1667 return false;
1668 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001669
caryclark@google.com18063442012-07-25 12:05:18 +00001670 bool decrementSpan(Span* span) {
1671 SkASSERT(span->fWindValue > 0);
1672 if (--(span->fWindValue) == 0) {
1673 span->fDone = true;
1674 ++fDoneSpans;
1675 return true;
1676 }
1677 return false;
1678 }
1679
caryclark@google.com15fa1382012-05-07 20:49:36 +00001680 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001681 SkASSERT(fDoneSpans <= fTs.count());
1682 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001683 }
1684
caryclark@google.com47580692012-07-23 12:14:49 +00001685 bool done(const Angle& angle) const {
1686 int start = angle.start();
1687 int end = angle.end();
1688 const Span& mSpan = fTs[SkMin32(start, end)];
1689 return mSpan.fDone;
1690 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00001691
caryclark@google.com235f56a2012-09-14 14:19:30 +00001692 Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
1693 int& nextStart, int& nextEnd, int& winding, int& spanWinding, ShapeOp op,
1694 const int aXorMask, const int bXorMask) {
1695 const int startIndex = nextStart;
1696 const int endIndex = nextEnd;
1697 int outerWinding = winding;
1698 int innerWinding = winding + spanWinding;
1699 #if DEBUG_WINDING
1700 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1701 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
1702 #endif
1703 if (useInnerWinding(outerWinding, innerWinding)) {
1704 outerWinding = innerWinding;
1705 }
1706 SkASSERT(startIndex != endIndex);
1707 int count = fTs.count();
1708 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1709 : startIndex > 0);
1710 int step = SkSign32(endIndex - startIndex);
1711 int end = nextSpan(startIndex, step);
1712 SkASSERT(end >= 0);
1713 Span* endSpan = &fTs[end];
1714 Segment* other;
1715 if (isSimple(end)) {
1716 // mark the smaller of startIndex, endIndex done, and all adjacent
1717 // spans with the same T value (but not 'other' spans)
1718 #if DEBUG_WINDING
1719 SkDebugf("%s simple\n", __FUNCTION__);
1720 #endif
1721 markDone(SkMin32(startIndex, endIndex), outerWinding);
1722 other = endSpan->fOther;
1723 nextStart = endSpan->fOtherIndex;
1724 double startT = other->fTs[nextStart].fT;
1725 nextEnd = nextStart;
1726 do {
1727 nextEnd += step;
1728 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
1729 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1730 return other;
1731 }
1732 // more than one viable candidate -- measure angles to find best
1733 SkTDArray<Angle> angles;
1734 SkASSERT(startIndex - endIndex != 0);
1735 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1736 addTwoAngles(startIndex, end, angles);
1737 buildAngles(end, angles);
1738 SkTDArray<Angle*> sorted;
1739 sortAngles(angles, sorted);
1740 int angleCount = angles.count();
1741 int firstIndex = findStartingEdge(sorted, startIndex, end);
1742 SkASSERT(firstIndex >= 0);
1743 #if DEBUG_SORT
1744 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
1745 #endif
1746 SkASSERT(sorted[firstIndex]->segment() == this);
1747 #if DEBUG_WINDING
1748 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
1749 #endif
1750 int aSumWinding = winding;
1751 int bSumWinding = winding;
1752 bool angleIsOp = sorted[firstIndex]->segment()->operand();
1753 int angleSpan = spanSign(sorted[firstIndex]);
1754 if (angleIsOp) {
1755 bSumWinding -= angleSpan;
1756 } else {
1757 aSumWinding -= angleSpan;
1758 }
1759 int nextIndex = firstIndex + 1;
1760 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1761 const Angle* foundAngle = NULL;
1762 // FIXME: found done logic probably fails if there are more than 4
1763 // sorted angles. It should bias towards the first and last undone
1764 // edges -- but not sure that it won't choose a middle (incorrect)
1765 // edge if one is undone
1766 bool foundDone = false;
1767 bool foundDone2 = false;
1768 // iterate through the angle, and compute everyone's winding
1769 bool altFlipped = false;
1770 bool foundFlipped = false;
1771 int foundMax = SK_MinS32;
1772 int foundSum = SK_MinS32;
1773 Segment* nextSegment;
1774 int lastNonZeroSum = winding;
1775 do {
1776 if (nextIndex == angleCount) {
1777 nextIndex = 0;
1778 }
1779 const Angle* nextAngle = sorted[nextIndex];
1780 nextSegment = nextAngle->segment();
1781 angleIsOp = nextSegment->operand();
1782 int sumWinding = angleIsOp ? bSumWinding : aSumWinding;
1783 int maxWinding = sumWinding;
1784 if (sumWinding) {
1785 lastNonZeroSum = sumWinding;
1786 }
1787 sumWinding -= nextSegment->spanSign(nextAngle);
1788 int xorMask = nextSegment->operand() ? bXorMask : aXorMask;
1789 bool otherNonZero;
1790 if (angleIsOp) {
1791 bSumWinding = sumWinding;
1792 otherNonZero = aSumWinding & aXorMask;
1793 } else {
1794 aSumWinding = sumWinding;
1795 otherNonZero = bSumWinding & bXorMask;
1796 }
1797 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00001798 #if 0 && DEBUG_WINDING
caryclark@google.com235f56a2012-09-14 14:19:30 +00001799 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
1800 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
1801 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
1802 #endif
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00001803
caryclark@google.com235f56a2012-09-14 14:19:30 +00001804 if (!(sumWinding & xorMask) && activeOp(angleIsOp, otherNonZero, op)) {
1805 if (!active) {
1806 markDone(SkMin32(startIndex, endIndex), outerWinding);
1807 // FIXME: seems like a bug that this isn't calling userInnerWinding
1808 nextSegment->markWinding(SkMin32(nextAngle->start(),
1809 nextAngle->end()), maxWinding);
1810 #if DEBUG_WINDING
1811 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
1812 #endif
1813 return NULL;
1814 }
1815 if (!foundAngle || foundDone) {
1816 foundAngle = nextAngle;
1817 foundDone = nextSegment->done(*nextAngle);
1818 foundFlipped = altFlipped;
1819 foundMax = maxWinding;
1820 }
1821 continue;
1822 }
1823 if (!(maxWinding & xorMask) && (!foundAngle || foundDone2)
1824 && activeOp(angleIsOp, otherNonZero, op)) {
1825 #if DEBUG_WINDING
1826 if (foundAngle && foundDone2) {
1827 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
1828 }
1829 #endif
1830 foundAngle = nextAngle;
1831 foundDone2 = nextSegment->done(*nextAngle);
1832 foundFlipped = altFlipped;
1833 foundSum = sumWinding;
1834 }
1835 if (nextSegment->done()) {
1836 continue;
1837 }
1838 // if the winding is non-zero, nextAngle does not connect to
1839 // current chain. If we haven't done so already, mark the angle
1840 // as done, record the winding value, and mark connected unambiguous
1841 // segments as well.
1842 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
1843 if (useInnerWinding(maxWinding, sumWinding)) {
1844 maxWinding = sumWinding;
1845 }
1846 Span* last;
1847 if (foundAngle) {
1848 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
1849 } else {
1850 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
1851 }
1852 if (last) {
1853 *chase.append() = last;
1854 }
1855 }
1856 } while (++nextIndex != lastIndex);
1857 markDone(SkMin32(startIndex, endIndex), outerWinding);
1858 if (!foundAngle) {
1859 return NULL;
1860 }
1861 nextStart = foundAngle->start();
1862 nextEnd = foundAngle->end();
1863 nextSegment = foundAngle->segment();
1864 int flipped = foundFlipped ? -1 : 1;
1865 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
1866 SkMin32(nextStart, nextEnd));
1867 if (winding) {
1868 #if DEBUG_WINDING
1869 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
1870 if (foundSum == SK_MinS32) {
1871 SkDebugf("?");
1872 } else {
1873 SkDebugf("%d", foundSum);
1874 }
1875 SkDebugf(" foundMax=");
1876 if (foundMax == SK_MinS32) {
1877 SkDebugf("?");
1878 } else {
1879 SkDebugf("%d", foundMax);
1880 }
1881 SkDebugf("\n");
1882 #endif
1883 winding = foundSum;
1884 }
1885 #if DEBUG_WINDING
1886 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
1887 #endif
1888 return nextSegment;
1889 }
caryclark@google.com47580692012-07-23 12:14:49 +00001890
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001891 // so the span needs to contain the pairing info found here
1892 // this should include the winding computed for the edge, and
1893 // what edge it connects to, and whether it is discarded
1894 // (maybe discarded == abs(winding) > 1) ?
1895 // only need derivatives for duration of sorting, add a new struct
1896 // for pairings, remove extra spans that have zero length and
1897 // reference an unused other
1898 // for coincident, the last span on the other may be marked done
1899 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001900
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001901 // if loop is exhausted, contour may be closed.
1902 // FIXME: pass in close point so we can check for closure
1903
1904 // given a segment, and a sense of where 'inside' is, return the next
1905 // segment. If this segment has an intersection, or ends in multiple
1906 // segments, find the mate that continues the outside.
1907 // note that if there are multiples, but no coincidence, we can limit
1908 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00001909
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001910 // mark found segments as done
1911
caryclark@google.com15fa1382012-05-07 20:49:36 +00001912 // start is the index of the beginning T of this edge
1913 // it is guaranteed to have an end which describes a non-zero length (?)
1914 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00001915 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
1916 int& nextStart, int& nextEnd, int& winding, int& spanWinding) {
1917 const int startIndex = nextStart;
1918 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001919 int outerWinding = winding;
1920 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00001921 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001922 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1923 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00001924 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001925 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001926 outerWinding = innerWinding;
1927 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001928 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001929 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001930 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1931 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001932 int step = SkSign32(endIndex - startIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001933 int end = nextSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001934 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001935 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001936 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001937 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001938 // mark the smaller of startIndex, endIndex done, and all adjacent
1939 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001940 #if DEBUG_WINDING
1941 SkDebugf("%s simple\n", __FUNCTION__);
1942 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001943 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001944 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001945 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00001946 double startT = other->fTs[nextStart].fT;
1947 nextEnd = nextStart;
1948 do {
1949 nextEnd += step;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001950 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00001951 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001952 return other;
1953 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001954 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001955 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001956 SkASSERT(startIndex - endIndex != 0);
1957 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001958 addTwoAngles(startIndex, end, angles);
1959 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001960 SkTDArray<Angle*> sorted;
1961 sortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001962 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001963 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001964 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00001965 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001966 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001967 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001968 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001969 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001970 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00001971 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001972 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001973 int nextIndex = firstIndex + 1;
1974 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1975 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00001976 // FIXME: found done logic probably fails if there are more than 4
1977 // sorted angles. It should bias towards the first and last undone
1978 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001979 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00001980 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00001981 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001982 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00001983 bool altFlipped = false;
1984 bool foundFlipped = false;
1985 int foundMax = SK_MinS32;
1986 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001987 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00001988 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001989 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001990 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001991 nextIndex = 0;
1992 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001993 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00001994 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00001995 if (sumWinding) {
1996 lastNonZeroSum = sumWinding;
1997 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00001998 nextSegment = nextAngle->segment();
caryclark@google.com2ddff932012-08-07 21:25:27 +00001999 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002000 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002001 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002002 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002003 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2004 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002005 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002006 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002007 if (!active) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002008 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002009 // FIXME: seems like a bug that this isn't calling userInnerWinding
caryclark@google.com47580692012-07-23 12:14:49 +00002010 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002011 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002012 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002013 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002014 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002015 return NULL;
2016 }
caryclark@google.com47580692012-07-23 12:14:49 +00002017 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002018 foundAngle = nextAngle;
caryclark@google.com47580692012-07-23 12:14:49 +00002019 foundDone = nextSegment->done(*nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002020 foundFlipped = altFlipped;
2021 foundMax = maxWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002022 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002023 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002024 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002025 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002026 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002027 if (foundAngle && foundDone2) {
2028 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002029 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002030 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002031 foundAngle = nextAngle;
caryclark@google.com24bec792012-08-20 12:43:57 +00002032 foundDone2 = nextSegment->done(*nextAngle);
2033 foundFlipped = altFlipped;
2034 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002035 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002036 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002037 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002038 }
2039 // if the winding is non-zero, nextAngle does not connect to
2040 // current chain. If we haven't done so already, mark the angle
2041 // as done, record the winding value, and mark connected unambiguous
2042 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002043 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002044 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002045 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002046 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002047 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002048 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002049 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002050 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002051 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002052 }
2053 if (last) {
2054 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002055 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002056 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002057 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002058 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002059 if (!foundAngle) {
2060 return NULL;
2061 }
2062 nextStart = foundAngle->start();
2063 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002064 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002065 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002066 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2067 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002068 if (winding) {
2069 #if DEBUG_WINDING
2070 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2071 if (foundSum == SK_MinS32) {
2072 SkDebugf("?");
2073 } else {
2074 SkDebugf("%d", foundSum);
2075 }
2076 SkDebugf(" foundMax=");
2077 if (foundMax == SK_MinS32) {
2078 SkDebugf("?");
2079 } else {
2080 SkDebugf("%d", foundMax);
2081 }
2082 SkDebugf("\n");
2083 #endif
2084 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002085 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002086 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002087 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002088 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002089 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002090 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002091
caryclark@google.com24bec792012-08-20 12:43:57 +00002092 Segment* findNextXor(int& nextStart, int& nextEnd) {
2093 const int startIndex = nextStart;
2094 const int endIndex = nextEnd;
2095 SkASSERT(startIndex != endIndex);
2096 int count = fTs.count();
2097 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2098 : startIndex > 0);
2099 int step = SkSign32(endIndex - startIndex);
2100 int end = nextSpan(startIndex, step);
2101 SkASSERT(end >= 0);
2102 Span* endSpan = &fTs[end];
2103 Segment* other;
2104 markDone(SkMin32(startIndex, endIndex), 1);
2105 if (isSimple(end)) {
2106 #if DEBUG_WINDING
2107 SkDebugf("%s simple\n", __FUNCTION__);
2108 #endif
2109 other = endSpan->fOther;
2110 nextStart = endSpan->fOtherIndex;
2111 double startT = other->fTs[nextStart].fT;
2112 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002113 if ((approximately_less_than_zero(startT) && step < 0)
2114 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002115 step = -step;
2116 SkDEBUGCODE(firstLoop = false;)
2117 }
2118 do {
2119 nextEnd = nextStart;
2120 do {
2121 nextEnd += step;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002122 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002123 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2124 break;
2125 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002126 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002127 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002128 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002129 SkDEBUGCODE(firstLoop = false;)
2130 step = -step;
2131 } while (true);
2132 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2133 return other;
2134 }
2135 SkTDArray<Angle> angles;
2136 SkASSERT(startIndex - endIndex != 0);
2137 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2138 addTwoAngles(startIndex, end, angles);
2139 buildAngles(end, angles);
2140 SkTDArray<Angle*> sorted;
2141 sortAngles(angles, sorted);
2142 int angleCount = angles.count();
2143 int firstIndex = findStartingEdge(sorted, startIndex, end);
2144 SkASSERT(firstIndex >= 0);
2145 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002146 debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002147 #endif
2148 SkASSERT(sorted[firstIndex]->segment() == this);
2149 int nextIndex = firstIndex + 1;
2150 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2151 const Angle* nextAngle;
2152 Segment* nextSegment;
2153 do {
2154 if (nextIndex == angleCount) {
2155 nextIndex = 0;
2156 }
2157 nextAngle = sorted[nextIndex];
2158 nextSegment = nextAngle->segment();
2159 if (!nextSegment->done(*nextAngle)) {
2160 break;
2161 }
2162 if (++nextIndex == lastIndex) {
2163 return NULL;
2164 }
2165 } while (true);
2166 nextStart = nextAngle->start();
2167 nextEnd = nextAngle->end();
2168 return nextSegment;
2169 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002170
2171 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2172 int angleCount = sorted.count();
2173 int firstIndex = -1;
2174 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2175 const Angle* angle = sorted[angleIndex];
2176 if (angle->segment() == this && angle->start() == end &&
2177 angle->end() == start) {
2178 firstIndex = angleIndex;
2179 break;
2180 }
2181 }
2182 return firstIndex;
2183 }
2184
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002185 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com235f56a2012-09-14 14:19:30 +00002186 void findTooCloseToCall(bool isXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002187 int count = fTs.count();
2188 if (count < 3) { // require t=0, x, 1 at minimum
2189 return;
2190 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002191 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002192 int moCount;
2193 Span* match;
2194 Segment* mOther;
2195 do {
2196 match = &fTs[matchIndex];
2197 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002198 // FIXME: allow quads, cubics to be near coincident?
2199 if (mOther->fVerb == SkPath::kLine_Verb) {
2200 moCount = mOther->fTs.count();
2201 if (moCount >= 3) {
2202 break;
2203 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002204 }
2205 if (++matchIndex >= count) {
2206 return;
2207 }
2208 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002209 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002210 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002211 // look for a pair of nearby T values that map to the same (x,y) value
2212 // if found, see if the pair of other segments share a common point. If
2213 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002214 for (int index = matchIndex + 1; index < count; ++index) {
2215 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002216 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002217 continue;
2218 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002219 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002220 if (tOther->fVerb != SkPath::kLine_Verb) {
2221 continue; // FIXME: allow quads, cubics to be near coincident?
2222 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002223 int toCount = tOther->fTs.count();
2224 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002225 continue;
2226 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002227 const SkPoint* testPt = &xyAtT(test);
2228 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002229 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002230 moCount = toCount;
2231 match = test;
2232 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002233 matchPt = testPt;
2234 continue;
2235 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002236 int moStart = -1;
2237 int moEnd = -1;
2238 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002239 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002240 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002241 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002242 continue;
2243 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002244 if (moSpan.fOther == this) {
2245 if (moSpan.fOtherT == match->fT) {
2246 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002247 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002248 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002249 continue;
2250 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002251 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002252 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2253 moStart = -1;
2254 break;
2255 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002256 SkASSERT(moEnd == -1);
2257 moEnd = moIndex;
2258 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002259 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002260 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002261 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002262 continue;
2263 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002264 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002265 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002266 continue;
2267 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002268 int toStart = -1;
2269 int toEnd = -1;
2270 double toStartT, toEndT;
2271 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2272 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002273 if (toSpan.fDone) {
2274 continue;
2275 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002276 if (toSpan.fOther == this) {
2277 if (toSpan.fOtherT == test->fT) {
2278 toStart = toIndex;
2279 toStartT = toSpan.fT;
2280 }
2281 continue;
2282 }
2283 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002284 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2285 moStart = -1;
2286 break;
2287 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002288 SkASSERT(toEnd == -1);
2289 toEnd = toIndex;
2290 toEndT = toSpan.fT;
2291 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002292 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002293 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2294 if (toStart <= 0 || toEnd <= 0) {
2295 continue;
2296 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002297 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002298 continue;
2299 }
2300 // test to see if the segment between there and here is linear
2301 if (!mOther->isLinear(moStart, moEnd)
2302 || !tOther->isLinear(toStart, toEnd)) {
2303 continue;
2304 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002305 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002306 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002307 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002308 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002309 // FIXME: this is bogus for multiple ops
2310 // the xorMask needs to be accumulated from the union of the two
2311 // edges -- which means that the segment must have its own copy of the mask
2312 mOther->addTCoincident(isXor, moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002313 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002314 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002315 }
2316
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002317 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2318 // and use more concise logic like the old edge walker code?
2319 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002320 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002321 // iterate through T intersections and return topmost
2322 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002323 SkASSERT(!done());
2324 int firstT;
2325 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002326 SkPoint topPt;
2327 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002328 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002329 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002330 bool lastDone = true;
2331 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002332 const Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002333 if (!span.fDone || !lastDone) {
2334 const SkPoint& intercept = xyAtT(&span);
2335 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2336 && topPt.fX > intercept.fX)) {
2337 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002338 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002339 } else if (topPt == intercept) {
2340 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002341 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002342 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002343 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002344 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002345 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002346 int step = 1;
2347 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002348 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002349 step = -1;
2350 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002351 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002352 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002353 // if the topmost T is not on end, or is three-way or more, find left
2354 // look for left-ness from tLeft to firstT (matching y of other)
2355 SkTDArray<Angle> angles;
2356 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002357 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002358 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002359 SkTDArray<Angle*> sorted;
2360 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002361 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00002362 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002363 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002364 // skip edges that have already been processed
2365 firstT = -1;
2366 Segment* leftSegment;
2367 do {
2368 const Angle* angle = sorted[++firstT];
2369 leftSegment = angle->segment();
2370 tIndex = angle->end();
2371 endIndex = angle->start();
2372 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002373 return leftSegment;
2374 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002375
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002376 // FIXME: not crazy about this
2377 // when the intersections are performed, the other index is into an
2378 // incomplete array. as the array grows, the indices become incorrect
2379 // while the following fixes the indices up again, it isn't smart about
2380 // skipping segments whose indices are already correct
2381 // assuming we leave the code that wrote the index in the first place
2382 void fixOtherTIndex() {
2383 int iCount = fTs.count();
2384 for (int i = 0; i < iCount; ++i) {
2385 Span& iSpan = fTs[i];
2386 double oT = iSpan.fOtherT;
2387 Segment* other = iSpan.fOther;
2388 int oCount = other->fTs.count();
2389 for (int o = 0; o < oCount; ++o) {
2390 Span& oSpan = other->fTs[o];
2391 if (oT == oSpan.fT && this == oSpan.fOther) {
2392 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002393 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002394 }
2395 }
2396 }
2397 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002398
caryclark@google.com495f8e42012-05-31 13:13:11 +00002399 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002400 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002401 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002402 SkASSERT(end >= 0);
2403 if (multipleSpans(end)) {
2404 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002405 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002406 const Span& endSpan = fTs[end];
2407 Segment* other = endSpan.fOther;
2408 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002409 int otherEnd = other->nextSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002410 Span* last = other->innerChaseDone(index, step, winding);
2411 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002412 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002413 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002414
caryclark@google.com59823f72012-08-09 18:17:47 +00002415 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002416 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002417 SkASSERT(end >= 0);
2418 if (multipleSpans(end)) {
2419 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002420 }
2421 const Span& endSpan = fTs[end];
2422 Segment* other = endSpan.fOther;
2423 index = endSpan.fOtherIndex;
2424 int otherEnd = other->nextSpan(index, step);
2425 int min = SkMin32(index, otherEnd);
2426 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002427 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002428 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002429 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002430 Span* last = other->innerChaseWinding(index, step, winding);
2431 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002432 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002433 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002434
caryclark@google.com235f56a2012-09-14 14:19:30 +00002435 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2436 fDoneSpans = 0;
2437 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002438 fPts = pts;
2439 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002440 }
2441
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002442 bool intersected() const {
2443 return fTs.count() > 0;
2444 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002445
2446 bool isConnected(int startIndex, int endIndex) const {
2447 return fTs[startIndex].fWindSum != SK_MinS32
2448 || fTs[endIndex].fWindSum != SK_MinS32;
2449 }
2450
caryclark@google.com235f56a2012-09-14 14:19:30 +00002451 bool isHorizontal() const {
2452 return fBounds.fTop == fBounds.fBottom;
2453 }
2454
caryclark@google.com15fa1382012-05-07 20:49:36 +00002455 bool isLinear(int start, int end) const {
2456 if (fVerb == SkPath::kLine_Verb) {
2457 return true;
2458 }
2459 if (fVerb == SkPath::kQuad_Verb) {
2460 SkPoint qPart[3];
2461 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2462 return QuadIsLinear(qPart);
2463 } else {
2464 SkASSERT(fVerb == SkPath::kCubic_Verb);
2465 SkPoint cPart[4];
2466 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2467 return CubicIsLinear(cPart);
2468 }
2469 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002470
2471 // OPTIMIZE: successive calls could start were the last leaves off
2472 // or calls could specialize to walk forwards or backwards
2473 bool isMissing(double startT) const {
2474 size_t tCount = fTs.count();
2475 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002476 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002477 return false;
2478 }
2479 }
2480 return true;
2481 }
2482
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002483 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002484 int count = fTs.count();
2485 if (count == 2) {
2486 return true;
2487 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002488 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002489 if (approximately_less_than_zero(t)) {
2490 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002491 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002492 if (approximately_greater_than_one(t)) {
2493 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002494 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002495 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002496 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002497
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002498 bool isVertical() const {
2499 return fBounds.fLeft == fBounds.fRight;
2500 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002501
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002502 SkScalar leftMost(int start, int end) const {
2503 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2504 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002505
caryclark@google.com495f8e42012-05-31 13:13:11 +00002506 // this span is excluded by the winding rule -- chase the ends
2507 // as long as they are unambiguous to mark connections as done
2508 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002509 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002510 int index = angle->start();
2511 int endIndex = angle->end();
2512 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002513 Span* last = innerChaseDone(index, step, winding);
2514 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002515 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002516 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002517
caryclark@google.com59823f72012-08-09 18:17:47 +00002518 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002519 int index = angle->start();
2520 int endIndex = angle->end();
2521 int min = SkMin32(index, endIndex);
2522 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002523 Span* last = innerChaseWinding(index, step, winding);
2524 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002525 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002526 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002527
caryclark@google.com495f8e42012-05-31 13:13:11 +00002528 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002529 // This may be called when the segment is already marked done. While this
2530 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002531 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002532 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002533 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002534 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002535 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002536 double referenceT = fTs[index].fT;
2537 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002538 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002539 markOneDone(__FUNCTION__, lesser, winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002540 }
2541 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002542 markOneDone(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002543 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002544 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002545
caryclark@google.com24bec792012-08-20 12:43:57 +00002546 void markOneDone(const char* funName, int tIndex, int winding) {
2547 Span* span = markOneWinding(funName, tIndex, winding);
2548 if (!span) {
2549 return;
2550 }
2551 span->fDone = true;
2552 fDoneSpans++;
2553 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002554
caryclark@google.com24bec792012-08-20 12:43:57 +00002555 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2556 Span& span = fTs[tIndex];
2557 if (span.fDone) {
2558 return NULL;
2559 }
2560 #if DEBUG_MARK_DONE
2561 debugShowNewWinding(funName, span, winding);
2562 #endif
2563 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002564 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002565 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002566 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002567 span.fWindSum = winding;
2568 return &span;
2569 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002570
caryclark@google.com59823f72012-08-09 18:17:47 +00002571 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002572 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002573 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002574 double referenceT = fTs[index].fT;
2575 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002576 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002577 markOneWinding(__FUNCTION__, lesser, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002578 }
2579 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002580 markOneWinding(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002581 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002582 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002583
caryclark@google.com2ddff932012-08-07 21:25:27 +00002584 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002585 int nextDoorWind = SK_MaxS32;
2586 if (tIndex > 0) {
2587 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002588 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002589 nextDoorWind = below.fWindValue;
2590 }
2591 }
2592 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2593 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002594 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002595 nextDoorWind = above.fWindValue;
2596 }
2597 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002598 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2599 const Span& below = fTs[tIndex - 1];
2600 nextDoorWind = below.fWindValue;
2601 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002602 if (nextDoorWind != SK_MaxS32) {
2603 Span& newSpan = fTs[tIndex];
2604 newSpan.fWindValue = nextDoorWind;
2605 if (!nextDoorWind) {
2606 newSpan.fDone = true;
2607 ++fDoneSpans;
2608 }
2609 }
2610 }
2611
caryclark@google.com9764cc62012-07-12 19:29:45 +00002612 // return span if when chasing, two or more radiating spans are not done
2613 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2614 // candidate and the remaining spans have windValue == 0 (canceled by
2615 // coincidence). The coincident edges could either be removed altogether,
2616 // or this code could be more complicated in detecting this case. Worth it?
2617 bool multipleSpans(int end) const {
2618 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002619 }
2620
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002621 // This has callers for two different situations: one establishes the end
2622 // of the current span, and one establishes the beginning of the next span
2623 // (thus the name). When this is looking for the end of the current span,
2624 // coincidence is found when the beginning Ts contain -step and the end
2625 // contains step. When it is looking for the beginning of the next, the
2626 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002627 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002628 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002629 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002630 int count = fTs.count();
2631 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002632 while (step > 0 ? ++to < count : --to >= 0) {
2633 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002634 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002635 continue;
2636 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002637 return to;
2638 }
2639 return -1;
2640 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002641
caryclark@google.com235f56a2012-09-14 14:19:30 +00002642 bool operand() const {
2643 return fOperand;
2644 }
2645
2646 int oppSign(int startIndex, int endIndex) const {
2647 int result = startIndex < endIndex ? -fTs[startIndex].fWindValueOpp :
2648 fTs[endIndex].fWindValueOpp;
2649#if DEBUG_WIND_BUMP
2650 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2651#endif
2652 return result;
2653 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002654
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002655 const SkPoint* pts() const {
2656 return fPts;
2657 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002658
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002659 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002660 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002661 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2662 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002663 }
2664
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002665 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002666 const Span& span(int tIndex) const {
2667 return fTs[tIndex];
2668 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002669
caryclark@google.com235f56a2012-09-14 14:19:30 +00002670 int spanSign(const Angle* angle) const {
2671 SkASSERT(angle->segment() == this);
2672 return spanSign(angle->start(), angle->end());
2673 }
2674
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002675 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002676 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue :
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002677 fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002678#if DEBUG_WIND_BUMP
2679 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2680#endif
2681 return result;
2682 }
2683
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002684 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002685 double t(int tIndex) const {
2686 return fTs[tIndex].fT;
2687 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002688
caryclark@google.com18063442012-07-25 12:05:18 +00002689 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
2690 double start) {
2691 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002692 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00002693 *outsideTs.append() = end;
2694 *outsideTs.append() = start;
2695 }
2696 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002697
caryclark@google.com24bec792012-08-20 12:43:57 +00002698 void undoneSpan(int& start, int& end) {
2699 size_t tCount = fTs.count();
2700 size_t index;
2701 for (index = 0; index < tCount; ++index) {
2702 if (!fTs[index].fDone) {
2703 break;
2704 }
2705 }
2706 SkASSERT(index < tCount - 1);
2707 start = index;
2708 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002709 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00002710 SkASSERT(index < tCount);
2711 SkASSERT(index < tCount);
2712 end = index;
2713 }
caryclark@google.com18063442012-07-25 12:05:18 +00002714
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002715 void updatePts(const SkPoint pts[]) {
2716 fPts = pts;
2717 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002718
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002719 SkPath::Verb verb() const {
2720 return fVerb;
2721 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002722
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002723 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002724 return fTs[tIndex].fWindSum;
2725 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002726
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002727 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002728 int start = angle->start();
2729 int end = angle->end();
2730 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002731 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002732 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002733
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002734 int windValue(int tIndex) const {
2735 return fTs[tIndex].fWindValue;
2736 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002737
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002738 int windValue(const Angle* angle) const {
2739 int start = angle->start();
2740 int end = angle->end();
2741 int index = SkMin32(start, end);
2742 return windValue(index);
2743 }
2744
2745 SkScalar xAtT(const Span* span) const {
2746 return xyAtT(span).fX;
2747 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002748
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002749 const SkPoint& xyAtT(int index) const {
2750 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002751 }
2752
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002753 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002754 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002755 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002756 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002757 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002758 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002759 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002760 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002761 }
2762 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002763 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002764 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002765
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002766 SkScalar yAtT(int index) const {
2767 return yAtT(&fTs[index]);
2768 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002769
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002770 SkScalar yAtT(const Span* span) const {
2771 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002772 }
2773
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002774#if DEBUG_DUMP
2775 void dump() const {
2776 const char className[] = "Segment";
2777 const int tab = 4;
2778 for (int i = 0; i < fTs.count(); ++i) {
2779 SkPoint out;
2780 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
2781 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002782 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002783 tab + sizeof(className), className, fID,
2784 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002785 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002786 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002787 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002788 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00002789 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002790 }
2791#endif
2792
caryclark@google.com47580692012-07-23 12:14:49 +00002793#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00002794 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002795 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00002796 for (int i = 0; i < fTs.count(); ++i) {
2797 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
2798 return;
2799 }
2800 }
2801 SkASSERT(0);
2802 }
2803#endif
2804
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002805#if DEBUG_DUMP
2806 int debugID() const {
2807 return fID;
2808 }
2809#endif
2810
caryclark@google.com24bec792012-08-20 12:43:57 +00002811#if DEBUG_WINDING
2812 void debugShowSums() const {
2813 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
2814 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
2815 for (int i = 0; i < fTs.count(); ++i) {
2816 const Span& span = fTs[i];
2817 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
2818 if (span.fWindSum == SK_MinS32) {
2819 SkDebugf("?");
2820 } else {
2821 SkDebugf("%d", span.fWindSum);
2822 }
2823 SkDebugf("]");
2824 }
2825 SkDebugf("\n");
2826 }
2827#endif
2828
caryclark@google.comcc905052012-07-25 20:59:42 +00002829#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002830 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00002831 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00002832 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00002833 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00002834 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
2835 }
2836 SkDebugf("\n");
2837 }
2838#endif
2839
caryclark@google.com027de222012-07-12 12:52:50 +00002840#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002841 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00002842 if (done()) {
2843 return;
2844 }
2845 for (int i = 0; i < fTs.count(); ++i) {
2846 if (fTs[i].fDone) {
2847 continue;
2848 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002849 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00002850 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2851 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2852 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2853 }
2854 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00002855 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00002856 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00002857 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002858 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
2859 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
2860 if (fTs[i].fWindSum == SK_MinS32) {
2861 SkDebugf("?");
2862 } else {
2863 SkDebugf("%d", fTs[i].fWindSum);
2864 }
2865 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00002866 }
2867 }
2868#endif
2869
caryclark@google.com0c803d02012-08-06 11:15:47 +00002870#if DEBUG_MARK_DONE
2871 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
2872 const SkPoint& pt = xyAtT(&span);
2873 SkDebugf("%s id=%d", fun, fID);
2874 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2875 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2876 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2877 }
2878 SkDebugf(") t=%1.9g (%1.9g,%1.9g) newWindSum=%d windSum=",
2879 span.fT, pt.fX, pt.fY, winding);
2880 if (span.fWindSum == SK_MinS32) {
2881 SkDebugf("?");
2882 } else {
2883 SkDebugf("%d", span.fWindSum);
2884 }
2885 SkDebugf(" windValue=%d\n", span.fWindValue);
2886 }
2887#endif
2888
caryclark@google.com47580692012-07-23 12:14:49 +00002889#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002890 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002891 const int contourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002892 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00002893 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002894 int lastSum = contourWinding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002895 int windSum = lastSum - spanSign(angles[first]);
caryclark@google.com03f97062012-08-21 13:13:52 +00002896 SkDebugf("%s %s contourWinding=%d sign=%d\n", fun, __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +00002897 contourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00002898 int index = first;
2899 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00002900 do {
2901 const Angle& angle = *angles[index];
2902 const Segment& segment = *angle.segment();
2903 int start = angle.start();
2904 int end = angle.end();
2905 const Span& sSpan = segment.fTs[start];
2906 const Span& eSpan = segment.fTs[end];
2907 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002908 if (!firstTime) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002909 lastSum = windSum;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002910 windSum -= segment.spanSign(&angle);
caryclark@google.comafe56de2012-07-24 18:11:03 +00002911 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002912 SkDebugf("%s [%d] id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
caryclark@google.comc899ad92012-08-23 15:24:42 +00002913 " sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
2914 __FUNCTION__, index, segment.fID, kLVerbStr[segment.fVerb],
2915 start, segment.xAtT(&sSpan),
2916 segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
2917 segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
2918 lastSum, windSum, useInnerWinding(lastSum, windSum)
2919 ? windSum : lastSum, mSpan.fDone);
2920#if DEBUG_ANGLE
2921 angle.debugShow(segment.xyAtT(&sSpan));
2922#endif
caryclark@google.com47580692012-07-23 12:14:49 +00002923 ++index;
2924 if (index == angles.count()) {
2925 index = 0;
2926 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002927 if (firstTime) {
2928 firstTime = false;
2929 }
caryclark@google.com47580692012-07-23 12:14:49 +00002930 } while (index != first);
2931 }
2932#endif
2933
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002934#if DEBUG_WINDING
2935 bool debugVerifyWinding(int start, int end, int winding) const {
2936 const Span& span = fTs[SkMin32(start, end)];
2937 int spanWinding = span.fWindSum;
2938 if (spanWinding == SK_MinS32) {
2939 return true;
2940 }
2941 int spanSign = SkSign32(start - end);
2942 int signedVal = spanSign * span.fWindValue;
2943 if (signedVal < 0) {
2944 spanWinding -= signedVal;
2945 }
2946 return span.fWindSum == winding;
2947 }
2948#endif
2949
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002950private:
2951 const SkPoint* fPts;
2952 SkPath::Verb fVerb;
2953 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002954 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00002955 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00002956 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002957#if DEBUG_DUMP
2958 int fID;
2959#endif
2960};
2961
caryclark@google.comb9738012012-07-03 19:53:30 +00002962class Contour;
2963
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002964struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00002965 Contour* fContours[2];
2966 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002967 double fTs[2][2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00002968 bool fXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002969};
2970
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002971class Contour {
2972public:
2973 Contour() {
2974 reset();
2975#if DEBUG_DUMP
2976 fID = ++gContourID;
2977#endif
2978 }
2979
2980 bool operator<(const Contour& rh) const {
2981 return fBounds.fTop == rh.fBounds.fTop
2982 ? fBounds.fLeft < rh.fBounds.fLeft
2983 : fBounds.fTop < rh.fBounds.fTop;
2984 }
2985
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002986 void addCoincident(int index, Contour* other, int otherIndex,
2987 const Intersections& ts, bool swap) {
2988 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00002989 coincidence.fContours[0] = this;
2990 coincidence.fContours[1] = other;
2991 coincidence.fSegments[0] = index;
2992 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00002993 if (fSegments[index].verb() == SkPath::kLine_Verb &&
2994 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
2995 // FIXME: coincident lines use legacy Ts instead of coincident Ts
2996 coincidence.fTs[swap][0] = ts.fT[0][0];
2997 coincidence.fTs[swap][1] = ts.fT[0][1];
2998 coincidence.fTs[!swap][0] = ts.fT[1][0];
2999 coincidence.fTs[!swap][1] = ts.fT[1][1];
3000 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3001 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3002 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3003 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3004 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3005 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3006 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003007 coincidence.fXor = fOperand == other->fOperand ? fXor : true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003008 }
3009
3010 void addCross(const Contour* crosser) {
3011#ifdef DEBUG_CROSS
3012 for (int index = 0; index < fCrosses.count(); ++index) {
3013 SkASSERT(fCrosses[index] != crosser);
3014 }
3015#endif
3016 *fCrosses.append() = crosser;
3017 }
3018
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003019 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003020 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003021 fContainsCurves = true;
3022 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003023
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003024 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003025 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003026 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003027 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003028
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003029 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3030 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3031 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003032
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003033 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003034 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003035 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003036 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003037 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003038
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003039 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3040 containsIntercepts();
3041 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3042 }
3043
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003044 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003045 return fBounds;
3046 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003047
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003048 void complete() {
3049 setBounds();
3050 fContainsIntercepts = false;
3051 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003052
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003053 void containsIntercepts() {
3054 fContainsIntercepts = true;
3055 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003056
rmistry@google.comd6176b02012-08-23 18:14:13 +00003057 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003058 int &tIndex, double& hitT) {
3059 int segmentCount = fSegments.count();
3060 const Segment* bestSegment = NULL;
3061 for (int test = 0; test < segmentCount; ++test) {
3062 Segment* testSegment = &fSegments[test];
3063 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003064 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003065 continue;
3066 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003067 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003068 continue;
3069 }
3070 if (bounds.fLeft > basePt.fX) {
3071 continue;
3072 }
3073 if (bounds.fRight < basePt.fX) {
3074 continue;
3075 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003076 if (bounds.fLeft == bounds.fRight) {
3077 continue;
3078 }
3079 #if 0
3080 bool leftHalf = bounds.fLeft == basePt.fX;
3081 bool rightHalf = bounds.fRight == basePt.fX;
3082 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3083 basePt, leftHalf, rightHalf)) {
3084 continue;
3085 }
3086 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003087 double testHitT;
3088 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3089 if (testT >= 0) {
3090 bestSegment = testSegment;
3091 tIndex = testT;
3092 hitT = testHitT;
3093 }
3094 }
3095 return bestSegment;
3096 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003097
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003098 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003099 for (int index = 0; index < fCrosses.count(); ++index) {
3100 if (fCrosses[index] == crosser) {
3101 return true;
3102 }
3103 }
3104 return false;
3105 }
3106
caryclark@google.com235f56a2012-09-14 14:19:30 +00003107 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003108 int segmentCount = fSegments.count();
3109 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003110 fSegments[sIndex].findTooCloseToCall(fXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003111 }
3112 }
3113
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003114 void fixOtherTIndex() {
3115 int segmentCount = fSegments.count();
3116 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3117 fSegments[sIndex].fixOtherTIndex();
3118 }
3119 }
3120
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003121 void reset() {
3122 fSegments.reset();
3123 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003124 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003125 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003126
caryclark@google.com235f56a2012-09-14 14:19:30 +00003127 // FIXME: for binary ops, need to keep both ops winding contributions separately
3128 // in edge array
3129 void resolveCoincidence() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003130 int count = fCoincidences.count();
3131 for (int index = 0; index < count; ++index) {
3132 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003133 Contour* thisContour = coincidence.fContours[0];
3134 Contour* otherContour = coincidence.fContours[1];
3135 int thisIndex = coincidence.fSegments[0];
3136 int otherIndex = coincidence.fSegments[1];
3137 Segment& thisOne = thisContour->fSegments[thisIndex];
3138 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003139 #if DEBUG_CONCIDENT
3140 thisOne.debugShowTs();
3141 other.debugShowTs();
3142 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003143 double startT = coincidence.fTs[0][0];
3144 double endT = coincidence.fTs[0][1];
caryclark@google.com32546db2012-08-31 20:55:07 +00003145 bool opposite = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003146 if (startT > endT) {
3147 SkTSwap<double>(startT, endT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003148 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003149 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003150 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003151 double oStartT = coincidence.fTs[1][0];
3152 double oEndT = coincidence.fTs[1][1];
3153 if (oStartT > oEndT) {
3154 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003155 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003156 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003157 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com32546db2012-08-31 20:55:07 +00003158 if (opposite) {
caryclark@google.comb9738012012-07-03 19:53:30 +00003159 // make sure startT and endT have t entries
caryclark@google.com32546db2012-08-31 20:55:07 +00003160 SkASSERT(opposite);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003161 if (startT > 0 || oEndT < 1
3162 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3163 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003164 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003165 if (oStartT > 0 || endT < 1
3166 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3167 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003168 }
3169 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003170 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003171 if (startT > 0 || oStartT > 0
3172 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003173 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003174 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003175 if (endT < 1 || oEndT < 1
3176 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003177 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003178 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003179 thisOne.addTCoincident(coincidence.fXor, startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003180 }
caryclark@google.com47580692012-07-23 12:14:49 +00003181 #if DEBUG_CONCIDENT
3182 thisOne.debugShowTs();
3183 other.debugShowTs();
3184 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003185 }
3186 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003187
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003188 const SkTArray<Segment>& segments() {
3189 return fSegments;
3190 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003191
caryclark@google.com235f56a2012-09-14 14:19:30 +00003192 void setOperand(bool isOp) {
3193 fOperand = isOp;
3194 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003195
caryclark@google.com235f56a2012-09-14 14:19:30 +00003196 void setXor(bool isXor) {
3197 fXor = isXor;
3198 }
3199
caryclark@google.com15fa1382012-05-07 20:49:36 +00003200 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3201 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003202 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003203 // segments' top, and not a true sort, so it could be ameniable to regular
3204 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003205 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003206 int segmentCount = fSegments.count();
3207 SkASSERT(segmentCount > 0);
3208 int best = -1;
3209 Segment* bestSegment = NULL;
3210 while (++best < segmentCount) {
3211 Segment* testSegment = &fSegments[best];
3212 if (testSegment->done()) {
3213 continue;
3214 }
3215 bestSegment = testSegment;
3216 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003217 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003218 if (!bestSegment) {
3219 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003220 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003221 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003222 for (int test = best + 1; test < segmentCount; ++test) {
3223 Segment* testSegment = &fSegments[test];
3224 if (testSegment->done()) {
3225 continue;
3226 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003227 if (testSegment->bounds().fTop > bestTop) {
3228 continue;
3229 }
3230 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003231 if (bestTop > testTop) {
3232 bestTop = testTop;
3233 bestSegment = testSegment;
3234 }
3235 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003236 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003237 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003238 }
3239
caryclark@google.com24bec792012-08-20 12:43:57 +00003240 Segment* undoneSegment(int& start, int& end) {
3241 int segmentCount = fSegments.count();
3242 for (int test = 0; test < segmentCount; ++test) {
3243 Segment* testSegment = &fSegments[test];
3244 if (testSegment->done()) {
3245 continue;
3246 }
3247 testSegment->undoneSpan(start, end);
3248 return testSegment;
3249 }
3250 return NULL;
3251 }
3252
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003253 int updateSegment(int index, const SkPoint* pts) {
3254 Segment& segment = fSegments[index];
3255 segment.updatePts(pts);
3256 return segment.verb() + 1;
3257 }
3258
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003259#if DEBUG_TEST
3260 SkTArray<Segment>& debugSegments() {
3261 return fSegments;
3262 }
3263#endif
3264
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003265#if DEBUG_DUMP
3266 void dump() {
3267 int i;
3268 const char className[] = "Contour";
3269 const int tab = 4;
3270 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
3271 for (i = 0; i < fSegments.count(); ++i) {
3272 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
3273 className, i);
3274 fSegments[i].dump();
3275 }
3276 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
3277 tab + sizeof(className), className,
3278 fBounds.fLeft, fBounds.fTop,
3279 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003280 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
3281 className, fContainsIntercepts);
3282 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
3283 className, fContainsCurves);
3284 }
3285#endif
3286
caryclark@google.com027de222012-07-12 12:52:50 +00003287#if DEBUG_ACTIVE_SPANS
3288 void debugShowActiveSpans() {
3289 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003290 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00003291 }
3292 }
3293#endif
3294
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003295protected:
3296 void setBounds() {
3297 int count = fSegments.count();
3298 if (count == 0) {
3299 SkDebugf("%s empty contour\n", __FUNCTION__);
3300 SkASSERT(0);
3301 // FIXME: delete empty contour?
3302 return;
3303 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003304 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003305 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003306 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003307 }
3308 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003309
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003310private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003311 SkTArray<Segment> fSegments;
3312 SkTDArray<Coincidence> fCoincidences;
3313 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003314 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003315 bool fContainsIntercepts;
3316 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003317 bool fOperand; // true for the second argument to a binary operator
3318 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003319#if DEBUG_DUMP
3320 int fID;
3321#endif
3322};
3323
3324class EdgeBuilder {
3325public:
3326
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003327EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003328 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003329 , fContours(contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003330 , fCurrentContour(NULL)
3331 , fOperand(false)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003332{
caryclark@google.com235f56a2012-09-14 14:19:30 +00003333 fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003334#if DEBUG_DUMP
3335 gContourID = 0;
3336 gSegmentID = 0;
3337#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00003338 fSecondHalf = preFetch();
3339}
3340
3341void addOperand(const SkPath& path) {
3342 fPath = &path;
3343 fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
3344 preFetch();
3345}
3346
3347void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003348 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003349 complete();
3350 if (fCurrentContour && !fCurrentContour->segments().count()) {
3351 fContours.pop_back();
3352 }
3353 // correct pointers in contours since fReducePts may have moved as it grew
3354 int cIndex = 0;
3355 int extraCount = fExtra.count();
3356 SkASSERT(extraCount == 0 || fExtra[0] == -1);
3357 int eIndex = 0;
3358 int rIndex = 0;
3359 while (++eIndex < extraCount) {
3360 int offset = fExtra[eIndex];
3361 if (offset < 0) {
3362 ++cIndex;
3363 continue;
3364 }
3365 fCurrentContour = &fContours[cIndex];
3366 rIndex += fCurrentContour->updateSegment(offset - 1,
3367 &fReducePts[rIndex]);
3368 }
3369 fExtra.reset(); // we're done with this
3370}
3371
3372ShapeOpMask xorMask() const {
3373 return fXorMask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003374}
3375
3376protected:
3377
3378void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003379 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003380 fCurrentContour->complete();
3381 fCurrentContour = NULL;
3382 }
3383}
3384
caryclark@google.com235f56a2012-09-14 14:19:30 +00003385// FIXME:remove once we can access path pts directly
3386int preFetch() {
3387 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003388 SkPoint pts[4];
3389 SkPath::Verb verb;
3390 do {
3391 verb = iter.next(pts);
3392 *fPathVerbs.append() = verb;
3393 if (verb == SkPath::kMove_Verb) {
3394 *fPathPts.append() = pts[0];
3395 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
3396 fPathPts.append(verb, &pts[1]);
3397 }
3398 } while (verb != SkPath::kDone_Verb);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003399 return fPathVerbs.count();
3400}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003401
caryclark@google.com235f56a2012-09-14 14:19:30 +00003402void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003403 SkPath::Verb reducedVerb;
3404 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003405 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003406 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003407 const SkPoint* finalCurveStart = NULL;
3408 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003409 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003410 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
3411 switch (verb) {
3412 case SkPath::kMove_Verb:
3413 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003414 if (!fCurrentContour) {
3415 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003416 fCurrentContour->setOperand(fOperand);
3417 fCurrentContour->setXor(fXorMask == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003418 *fExtra.append() = -1; // start new contour
3419 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003420 finalCurveEnd = pointsPtr++;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003421 continue;
3422 case SkPath::kLine_Verb:
3423 // skip degenerate points
3424 if (pointsPtr[-1].fX != pointsPtr[0].fX
3425 || pointsPtr[-1].fY != pointsPtr[0].fY) {
3426 fCurrentContour->addLine(&pointsPtr[-1]);
3427 }
3428 break;
3429 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00003430
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003431 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
3432 if (reducedVerb == 0) {
3433 break; // skip degenerate points
3434 }
3435 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00003436 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003437 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003438 break;
3439 }
3440 fCurrentContour->addQuad(&pointsPtr[-1]);
3441 break;
3442 case SkPath::kCubic_Verb:
3443 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
3444 if (reducedVerb == 0) {
3445 break; // skip degenerate points
3446 }
3447 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003448 *fExtra.append() =
3449 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003450 break;
3451 }
3452 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003453 *fExtra.append() =
3454 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003455 break;
3456 }
3457 fCurrentContour->addCubic(&pointsPtr[-1]);
3458 break;
3459 case SkPath::kClose_Verb:
3460 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003461 if (finalCurveStart && finalCurveEnd
3462 && *finalCurveStart != *finalCurveEnd) {
3463 *fReducePts.append() = *finalCurveStart;
3464 *fReducePts.append() = *finalCurveEnd;
3465 *fExtra.append() =
3466 fCurrentContour->addLine(fReducePts.end() - 2);
3467 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003468 complete();
3469 continue;
3470 default:
3471 SkDEBUGFAIL("bad verb");
3472 return;
3473 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003474 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003475 pointsPtr += verb;
3476 SkASSERT(fCurrentContour);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003477 if (verbPtr == endOfFirstHalf) {
3478 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003479 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003480 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003481}
3482
3483private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00003484 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003485 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003486 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003487 Contour* fCurrentContour;
3488 SkTArray<Contour>& fContours;
3489 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003490 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com235f56a2012-09-14 14:19:30 +00003491 ShapeOpMask fXorMask;
3492 int fSecondHalf;
3493 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003494};
3495
3496class Work {
3497public:
3498 enum SegmentType {
3499 kHorizontalLine_Segment = -1,
3500 kVerticalLine_Segment = 0,
3501 kLine_Segment = SkPath::kLine_Verb,
3502 kQuad_Segment = SkPath::kQuad_Verb,
3503 kCubic_Segment = SkPath::kCubic_Verb,
3504 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00003505
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003506 void addCoincident(Work& other, const Intersections& ts, bool swap) {
3507 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
3508 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003509
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003510 // FIXME: does it make sense to write otherIndex now if we're going to
3511 // fix it up later?
3512 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003513 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003514 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003515
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003516 // Avoid collapsing t values that are close to the same since
3517 // we walk ts to describe consecutive intersections. Since a pair of ts can
3518 // be nearly equal, any problems caused by this should be taken care
3519 // of later.
3520 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003521 int addT(double newT, const Work& other) {
3522 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003523 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003524
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003525 bool advance() {
3526 return ++fIndex < fLast;
3527 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003528
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003529 SkScalar bottom() const {
3530 return bounds().fBottom;
3531 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003532
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003533 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003534 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003535 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003536
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003537 const SkPoint* cubic() const {
3538 return fCubic;
3539 }
3540
3541 void init(Contour* contour) {
3542 fContour = contour;
3543 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003544 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003545 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003546
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003547 bool isAdjacent(const Work& next) {
3548 return fContour == next.fContour && fIndex + 1 == next.fIndex;
3549 }
3550
3551 bool isFirstLast(const Work& next) {
3552 return fContour == next.fContour && fIndex == 0
3553 && next.fIndex == fLast - 1;
3554 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003555
3556 SkScalar left() const {
3557 return bounds().fLeft;
3558 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003559
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003560 void promoteToCubic() {
3561 fCubic[0] = pts()[0];
3562 fCubic[2] = pts()[1];
3563 fCubic[3] = pts()[2];
3564 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
3565 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
3566 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
3567 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
3568 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003569
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003570 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003571 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003572 }
3573
3574 SkScalar right() const {
3575 return bounds().fRight;
3576 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003577
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003578 ptrdiff_t segmentIndex() const {
3579 return fIndex;
3580 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003581
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003582 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003583 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003584 SegmentType type = (SegmentType) segment.verb();
3585 if (type != kLine_Segment) {
3586 return type;
3587 }
3588 if (segment.isHorizontal()) {
3589 return kHorizontalLine_Segment;
3590 }
3591 if (segment.isVertical()) {
3592 return kVerticalLine_Segment;
3593 }
3594 return kLine_Segment;
3595 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003596
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003597 bool startAfter(const Work& after) {
3598 fIndex = after.fIndex;
3599 return advance();
3600 }
3601
3602 SkScalar top() const {
3603 return bounds().fTop;
3604 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003605
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003606 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003607 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003608 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003609
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003610 SkScalar x() const {
3611 return bounds().fLeft;
3612 }
3613
3614 bool xFlipped() const {
3615 return x() != pts()[0].fX;
3616 }
3617
3618 SkScalar y() const {
3619 return bounds().fTop;
3620 }
3621
3622 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003623 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003624 }
3625
3626protected:
3627 Contour* fContour;
3628 SkPoint fCubic[4];
3629 int fIndex;
3630 int fLast;
3631};
3632
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003633#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003634static void debugShowLineIntersection(int pts, const Work& wt,
3635 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003636 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003637 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
3638 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
3639 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
3640 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003641 return;
3642 }
3643 SkPoint wtOutPt, wnOutPt;
3644 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3645 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003646 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003647 __FUNCTION__,
3648 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3649 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
3650 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003651 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003652 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003653 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003654 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3655 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
3656 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003657 SkDebugf(" wnTs[1]=%g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003658 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003659 SkDebugf("\n");
3660}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003661#else
3662static void debugShowLineIntersection(int , const Work& ,
3663 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003664}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003665#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003666
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003667static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003668
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003669 if (test != next) {
3670 if (test->bounds().fBottom < next->bounds().fTop) {
3671 return false;
3672 }
3673 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
3674 return true;
3675 }
3676 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003677 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003678 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003679 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003680 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003681 Work wn;
3682 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003683 if (test == next && !wn.startAfter(wt)) {
3684 continue;
3685 }
3686 do {
3687 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
3688 continue;
3689 }
3690 int pts;
3691 Intersections ts;
3692 bool swap = false;
3693 switch (wt.segmentType()) {
3694 case Work::kHorizontalLine_Segment:
3695 swap = true;
3696 switch (wn.segmentType()) {
3697 case Work::kHorizontalLine_Segment:
3698 case Work::kVerticalLine_Segment:
3699 case Work::kLine_Segment: {
3700 pts = HLineIntersect(wn.pts(), wt.left(),
3701 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003702 debugShowLineIntersection(pts, wt, wn,
3703 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003704 break;
3705 }
3706 case Work::kQuad_Segment: {
3707 pts = HQuadIntersect(wn.pts(), wt.left(),
3708 wt.right(), wt.y(), wt.xFlipped(), ts);
3709 break;
3710 }
3711 case Work::kCubic_Segment: {
3712 pts = HCubicIntersect(wn.pts(), wt.left(),
3713 wt.right(), wt.y(), wt.xFlipped(), ts);
3714 break;
3715 }
3716 default:
3717 SkASSERT(0);
3718 }
3719 break;
3720 case Work::kVerticalLine_Segment:
3721 swap = true;
3722 switch (wn.segmentType()) {
3723 case Work::kHorizontalLine_Segment:
3724 case Work::kVerticalLine_Segment:
3725 case Work::kLine_Segment: {
3726 pts = VLineIntersect(wn.pts(), wt.top(),
3727 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003728 debugShowLineIntersection(pts, wt, wn,
3729 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003730 break;
3731 }
3732 case Work::kQuad_Segment: {
3733 pts = VQuadIntersect(wn.pts(), wt.top(),
3734 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3735 break;
3736 }
3737 case Work::kCubic_Segment: {
3738 pts = VCubicIntersect(wn.pts(), wt.top(),
3739 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3740 break;
3741 }
3742 default:
3743 SkASSERT(0);
3744 }
3745 break;
3746 case Work::kLine_Segment:
3747 switch (wn.segmentType()) {
3748 case Work::kHorizontalLine_Segment:
3749 pts = HLineIntersect(wt.pts(), wn.left(),
3750 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003751 debugShowLineIntersection(pts, wt, wn,
3752 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003753 break;
3754 case Work::kVerticalLine_Segment:
3755 pts = VLineIntersect(wt.pts(), wn.top(),
3756 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003757 debugShowLineIntersection(pts, wt, wn,
3758 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003759 break;
3760 case Work::kLine_Segment: {
3761 pts = LineIntersect(wt.pts(), wn.pts(), ts);
3762 debugShowLineIntersection(pts, wt, wn,
3763 ts.fT[1], ts.fT[0]);
3764 break;
3765 }
3766 case Work::kQuad_Segment: {
3767 swap = true;
3768 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
3769 break;
3770 }
3771 case Work::kCubic_Segment: {
3772 swap = true;
3773 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
3774 break;
3775 }
3776 default:
3777 SkASSERT(0);
3778 }
3779 break;
3780 case Work::kQuad_Segment:
3781 switch (wn.segmentType()) {
3782 case Work::kHorizontalLine_Segment:
3783 pts = HQuadIntersect(wt.pts(), wn.left(),
3784 wn.right(), wn.y(), wn.xFlipped(), ts);
3785 break;
3786 case Work::kVerticalLine_Segment:
3787 pts = VQuadIntersect(wt.pts(), wn.top(),
3788 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3789 break;
3790 case Work::kLine_Segment: {
3791 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
3792 break;
3793 }
3794 case Work::kQuad_Segment: {
3795 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
3796 break;
3797 }
3798 case Work::kCubic_Segment: {
3799 wt.promoteToCubic();
3800 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
3801 break;
3802 }
3803 default:
3804 SkASSERT(0);
3805 }
3806 break;
3807 case Work::kCubic_Segment:
3808 switch (wn.segmentType()) {
3809 case Work::kHorizontalLine_Segment:
3810 pts = HCubicIntersect(wt.pts(), wn.left(),
3811 wn.right(), wn.y(), wn.xFlipped(), ts);
3812 break;
3813 case Work::kVerticalLine_Segment:
3814 pts = VCubicIntersect(wt.pts(), wn.top(),
3815 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3816 break;
3817 case Work::kLine_Segment: {
3818 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
3819 break;
3820 }
3821 case Work::kQuad_Segment: {
3822 wn.promoteToCubic();
3823 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
3824 break;
3825 }
3826 case Work::kCubic_Segment: {
3827 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
3828 break;
3829 }
3830 default:
3831 SkASSERT(0);
3832 }
3833 break;
3834 default:
3835 SkASSERT(0);
3836 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003837 if (!foundCommonContour && pts > 0) {
3838 test->addCross(next);
3839 next->addCross(test);
3840 foundCommonContour = true;
3841 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003842 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00003843 if (pts == 2) {
3844 if (wn.segmentType() <= Work::kLine_Segment
3845 && wt.segmentType() <= Work::kLine_Segment) {
3846 wt.addCoincident(wn, ts, swap);
3847 continue;
3848 }
3849 if (wn.segmentType() == Work::kQuad_Segment
3850 && wt.segmentType() == Work::kQuad_Segment
3851 && ts.coincidentUsed() == 2) {
3852 wt.addCoincident(wn, ts, swap);
3853 continue;
3854 }
3855
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003856 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003857 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003858 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
3859 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003860 int testTAt = wt.addT(ts.fT[swap][pt], wn);
3861 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003862 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
3863 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003864 }
3865 } while (wn.advance());
3866 } while (wt.advance());
3867 return true;
3868}
3869
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003870// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003871// see if coincidence is formed by clipping non-concident segments
caryclark@google.com235f56a2012-09-14 14:19:30 +00003872static void coincidenceCheck(SkTDArray<Contour*>& contourList) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003873 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00003874 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003875 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00003876 contour->resolveCoincidence();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003877 }
3878 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
3879 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00003880 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003881 }
3882}
3883
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003884// project a ray from the top of the contour up and see if it hits anything
3885// note: when we compute line intersections, we keep track of whether
3886// two contours touch, so we need only look at contours not touching this one.
3887// OPTIMIZATION: sort contourList vertically to avoid linear walk
3888static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003889 const Segment* current, int index, int endIndex) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003890 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003891 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003892 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00003893 const Segment* test = NULL;
3894 int tIndex;
3895 double tHit;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003896 // bool checkCrosses = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003897 for (int cTest = 0; cTest < contourCount; ++cTest) {
3898 Contour* contour = contourList[cTest];
3899 if (basePt.fY < contour->bounds().fTop) {
3900 continue;
3901 }
3902 if (bestY > contour->bounds().fBottom) {
3903 continue;
3904 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003905#if 0
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003906 // even though the contours crossed, if spans cancel through concidence,
3907 // the contours may be not have any span links to chase, and the current
3908 // segment may be isolated. Detect this by seeing if current has
3909 // uninitialized wind sums. If so, project a ray instead of relying on
3910 // previously found intersections.
3911 if (baseContour == contour) {
3912 continue;
3913 }
3914 if (checkCrosses && baseContour->crosses(contour)) {
3915 if (current->isConnected(index, endIndex)) {
3916 continue;
3917 }
3918 checkCrosses = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003919 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003920#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003921 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
3922 if (next) {
3923 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003924 }
caryclark@google.com47580692012-07-23 12:14:49 +00003925 }
3926 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00003927 return 0;
3928 }
3929 int winding, windValue;
3930 // If the ray hit the end of a span, we need to construct the wheel of
3931 // angles to find the span closest to the ray -- even if there are just
3932 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003933 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003934 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00003935 SkTDArray<Angle> angles;
3936 int end = test->nextSpan(tIndex, 1);
3937 if (end < 0) {
3938 end = test->nextSpan(tIndex, -1);
3939 }
3940 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00003941 SkASSERT(angles.count() > 0);
3942 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
3943#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00003944 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00003945#endif
3946 return 0;
3947 }
caryclark@google.com47580692012-07-23 12:14:49 +00003948 test->buildAngles(tIndex, angles);
3949 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00003950 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00003951 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00003952 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00003953 // hour after 6 o'clock
3954 sortAngles(angles, sorted);
3955#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00003956 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00003957#endif
3958 // walk the sorted angle fan to find the lowest angle
3959 // above the base point. Currently, the first angle in the sorted array
3960 // is 12 noon or an earlier hour (the next counterclockwise)
3961 int count = sorted.count();
3962 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003963 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00003964 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003965 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00003966 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00003967 angle = sorted[index];
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003968 if (baseMatches && angle->isHorizontal()) {
3969 continue;
3970 }
3971 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00003972 test = angle->segment();
3973 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
3974 const SkPoint* pts = test->pts();
3975 indexDx = pts[2].fX - pts[1].fX - indexDx;
3976 }
caryclark@google.com47580692012-07-23 12:14:49 +00003977 if (indexDx < 0) {
3978 left = index;
3979 } else if (indexDx > 0) {
3980 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00003981 int previous = index - 1;
3982 if (previous < 0) {
3983 previous = count - 1;
3984 }
3985 const Angle* prev = sorted[previous];
3986 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
3987#if DEBUG_SORT
3988 SkDebugf("%s use prev\n", __FUNCTION__);
3989#endif
3990 right = previous;
3991 }
caryclark@google.com47580692012-07-23 12:14:49 +00003992 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003993 } else {
3994 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003995 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003996 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003997 if (left < 0 && right < 0) {
3998 left = mid;
3999 }
caryclark@google.com47580692012-07-23 12:14:49 +00004000 SkASSERT(left >= 0 || right >= 0);
4001 if (left < 0) {
4002 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004003 } else if (left >= 0 && mid >= 0 && right >= 0
4004 && sorted[mid]->sign() == sorted[right]->sign()) {
4005 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004006 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004007 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004008 test = angle->segment();
4009 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004010 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004011 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004012#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004013 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4014 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004015#endif
4016 } else {
4017 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004018 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004019 windValue = test->windValue(tIndex);
4020#if DEBUG_WINDING
4021 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4022 windValue);
4023#endif
4024 }
4025 // see if a + change in T results in a +/- change in X (compute x'(T))
4026 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004027 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4028 const SkPoint* pts = test->pts();
4029 dx = pts[2].fX - pts[1].fX - dx;
4030 }
caryclark@google.com47580692012-07-23 12:14:49 +00004031#if DEBUG_WINDING
4032 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4033#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004034 SkASSERT(dx != 0);
4035 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004036 winding += dx > 0 ? -windValue : windValue;
4037#if DEBUG_WINDING
4038 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4039#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004040 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004041 return winding;
4042}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004043
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004044// OPTIMIZATION: not crazy about linear search here to find top active y.
4045// seems like we should break down and do the sort, or maybe sort each
rmistry@google.comd6176b02012-08-23 18:14:13 +00004046// contours' segments?
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004047// Once the segment array is built, there's no reason I can think of not to
4048// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004049// FIXME: return the contour found to pass to inner contour check
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004050static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004051 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004052 int cIndex = 0;
4053 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004054 SkScalar bestY = SK_ScalarMax;
4055 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004056 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004057 contour = contourList[cIndex];
4058 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004059 } while (!topStart && ++cIndex < contourCount);
4060 if (!topStart) {
4061 return NULL;
4062 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004063 while (++cIndex < contourCount) {
4064 contour = contourList[cIndex];
4065 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004066 continue;
4067 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004068 SkScalar testY = SK_ScalarMax;
4069 Segment* test = contour->topSegment(testY);
4070 if (!test || bestY <= testY) {
4071 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004072 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004073 topStart = test;
4074 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004075 }
4076 return topStart;
4077}
4078
caryclark@google.com24bec792012-08-20 12:43:57 +00004079static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4080 int contourCount = contourList.count();
4081 Segment* result;
4082 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4083 Contour* contour = contourList[cIndex];
4084 result = contour->undoneSegment(start, end);
4085 if (result) {
4086 return result;
4087 }
4088 }
4089 return NULL;
4090}
4091
4092
4093
caryclark@google.come21cb182012-07-23 21:26:31 +00004094static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
4095 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004096 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004097 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004098 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4099 Segment* segment = backPtr.fOther;
4100 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004101 SkTDArray<Angle> angles;
4102 int done = 0;
4103 if (segment->activeAngle(tIndex, done, angles)) {
4104 Angle* last = angles.end() - 1;
4105 tIndex = last->start();
4106 endIndex = last->end();
4107 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004108 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004109 if (done == angles.count()) {
4110 chase.pop(&span);
4111 continue;
4112 }
4113 SkTDArray<Angle*> sorted;
4114 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004115#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004116 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004117#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004118 // find first angle, initialize winding to computed fWindSum
4119 int firstIndex = -1;
4120 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004121 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004122 do {
4123 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004124 segment = angle->segment();
4125 winding = segment->windSum(angle);
4126 } while (winding == SK_MinS32);
4127 int spanWinding = segment->spanSign(angle->start(), angle->end());
4128 #if DEBUG_WINDING
4129 SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
4130 __FUNCTION__, winding, spanWinding, contourWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00004131 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004132 // turn swinding into contourWinding
4133 if (spanWinding * winding < 0) {
4134 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004135 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004136 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004137 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004138 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004139 // we care about first sign and whether wind sum indicates this
4140 // edge is inside or outside. Maybe need to pass span winding
4141 // or first winding or something into this function?
4142 // advance to first undone angle, then return it and winding
4143 // (to set whether edges are active or not)
4144 int nextIndex = firstIndex + 1;
4145 int angleCount = sorted.count();
4146 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004147 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00004148 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00004149 do {
4150 SkASSERT(nextIndex != firstIndex);
4151 if (nextIndex == angleCount) {
4152 nextIndex = 0;
4153 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004154 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00004155 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004156 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00004157 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004158 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00004159 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
4160 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004161 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004162 tIndex = angle->start();
4163 endIndex = angle->end();
4164 int lesser = SkMin32(tIndex, endIndex);
4165 const Span& nextSpan = segment->span(lesser);
4166 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004167#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00004168 // FIXME: this be wrong. assign startWinding if edge is in
4169 // same direction. If the direction is opposite, winding to
4170 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00004171 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004172 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004173 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004174 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004175#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004176 break;
4177 }
4178 } while (++nextIndex != lastIndex);
4179 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004180 }
4181 return NULL;
4182}
4183
caryclark@google.com027de222012-07-12 12:52:50 +00004184#if DEBUG_ACTIVE_SPANS
4185static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
4186 for (int index = 0; index < contourList.count(); ++ index) {
4187 contourList[index]->debugShowActiveSpans();
4188 }
4189}
4190#endif
4191
caryclark@google.com27c449a2012-07-27 18:26:38 +00004192static bool windingIsActive(int winding, int spanWinding) {
4193 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
4194 && (!winding || !spanWinding || winding == -spanWinding);
4195}
4196
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004197// Each segment may have an inside or an outside. Segments contained within
4198// winding may have insides on either side, and form a contour that should be
4199// ignored. Segments that are coincident with opposing direction segments may
4200// have outsides on either side, and should also disappear.
4201// 'Normal' segments will have one inside and one outside. Subsequent connections
4202// when winding should follow the intersection direction. If more than one edge
4203// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004204 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00004205 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com24bec792012-08-20 12:43:57 +00004206static void bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004207 bool firstContour = true;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004208 do {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004209 Segment* topStart = findTopContour(contourList);
caryclark@google.com15fa1382012-05-07 20:49:36 +00004210 if (!topStart) {
4211 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00004212 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004213 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00004214 // follow edges to intersection by changing the index by direction.
4215 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004216 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004217 int contourWinding;
4218 if (firstContour) {
4219 contourWinding = 0;
4220 firstContour = false;
4221 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004222 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004223 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00004224 if (sumWinding == SK_MinS32) {
4225 sumWinding = current->computeSum(index, endIndex);
4226 }
4227 if (sumWinding == SK_MinS32) {
4228 contourWinding = innerContourCheck(contourList, current,
4229 index, endIndex);
4230 } else {
4231 contourWinding = sumWinding;
4232 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004233 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
4234 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00004235 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004236 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004237#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004238 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004239 sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00004240 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004241#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004242 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004243#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004244 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004245 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
4246#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004247 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004248 SkPoint lastPt;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004249 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004250 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004251 // FIXME: needs work. While it works in limited situations, it does
4252 // not always compute winding correctly. Active should be removed and instead
4253 // the initial winding should be correctly passed in so that if the
4254 // inner contour is wound the same way, it never finds an accumulated
4255 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00004256 // other than zero when resolving sorted angles.
caryclark@google.com27c449a2012-07-27 18:26:38 +00004257 bool active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004258 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004259 do {
caryclark@google.com0e08a192012-07-13 21:07:52 +00004260 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00004261 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
4262 __FUNCTION__, active ? "true" : "false",
4263 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004264 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004265 const SkPoint* firstPt = NULL;
4266 do {
4267 SkASSERT(!current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004268 int nextStart = index;
4269 int nextEnd = endIndex;
4270 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.com27c449a2012-07-27 18:26:38 +00004271 nextStart, nextEnd, winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004272 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00004273 if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
4274 lastPt = current->addCurveTo(index, endIndex, simple, true);
4275 SkASSERT(*firstPt == lastPt);
4276 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004277 break;
4278 }
4279 if (!firstPt) {
4280 firstPt = &current->addMoveTo(index, simple, active);
4281 }
4282 lastPt = current->addCurveTo(index, endIndex, simple, active);
4283 current = next;
4284 index = nextStart;
4285 endIndex = nextEnd;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004286 } while (*firstPt != lastPt && (active || !current->done()));
4287 if (firstPt && active) {
4288 #if DEBUG_PATH_CONSTRUCTION
4289 SkDebugf("%s close\n", __FUNCTION__);
4290 #endif
4291 simple.close();
4292 }
caryclark@google.come21cb182012-07-23 21:26:31 +00004293 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004294 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00004295 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004296 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004297 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00004298 break;
4299 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004300 int lesser = SkMin32(index, endIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004301 spanWinding = current->spanSign(index, endIndex);
4302 winding = current->windSum(lesser);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004303 bool inner = useInnerWinding(winding - spanWinding, winding);
4304 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004305 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
caryclark@google.com59823f72012-08-09 18:17:47 +00004306 " inner=%d result=%d\n",
caryclark@google.com2ddff932012-08-07 21:25:27 +00004307 __FUNCTION__, current->debugID(), current->t(lesser),
4308 spanWinding, winding, SkSign32(index - endIndex),
4309 useInnerWinding(winding - spanWinding, winding),
caryclark@google.com2ddff932012-08-07 21:25:27 +00004310 inner ? winding - spanWinding : winding);
4311 #endif
4312 if (inner) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004313 winding -= spanWinding;
4314 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004315 active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004316 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004317 } while (true);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004318}
4319
caryclark@google.com24bec792012-08-20 12:43:57 +00004320static void bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
4321 Segment* current;
4322 int start, end;
4323 while ((current = findUndone(contourList, start, end))) {
4324 const SkPoint* firstPt = NULL;
4325 SkPoint lastPt;
4326 do {
4327 SkASSERT(!current->done());
4328 int nextStart = start;
4329 int nextEnd = end;
4330 Segment* next = current->findNextXor(nextStart, nextEnd);
4331 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00004332 if (firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
4333 lastPt = current->addCurveTo(start, end, simple, true);
4334 SkASSERT(*firstPt == lastPt);
4335 }
caryclark@google.com24bec792012-08-20 12:43:57 +00004336 break;
4337 }
4338 if (!firstPt) {
4339 firstPt = &current->addMoveTo(start, simple, true);
4340 }
4341 lastPt = current->addCurveTo(start, end, simple, true);
4342 current = next;
4343 start = nextStart;
4344 end = nextEnd;
4345 } while (*firstPt != lastPt);
4346 if (firstPt) {
4347 #if DEBUG_PATH_CONSTRUCTION
4348 SkDebugf("%s close\n", __FUNCTION__);
4349 #endif
4350 simple.close();
4351 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004352 }
caryclark@google.com24bec792012-08-20 12:43:57 +00004353}
4354
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004355static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
4356 int contourCount = contourList.count();
4357 for (int cTest = 0; cTest < contourCount; ++cTest) {
4358 Contour* contour = contourList[cTest];
4359 contour->fixOtherTIndex();
4360 }
4361}
4362
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004363static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004364 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004365 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004366 if (count == 0) {
4367 return;
4368 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004369 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004370 *list.append() = &contours[index];
4371 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004372 QSort<Contour>(list.begin(), list.end() - 1);
4373}
4374
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004375void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004376 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004377 simple.reset();
4378 simple.setFillType(SkPath::kEvenOdd_FillType);
4379
4380 // turn path into list of segments
4381 SkTArray<Contour> contours;
4382 // FIXME: add self-intersecting cubics' T values to segment
4383 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004384 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004385 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004386 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004387 Contour** currentPtr = contourList.begin();
4388 if (!currentPtr) {
4389 return;
4390 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004391 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004392 // find all intersections between segments
4393 do {
4394 Contour** nextPtr = currentPtr;
4395 Contour* current = *currentPtr++;
4396 Contour* next;
4397 do {
4398 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004399 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004400 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004401 // eat through coincident edges
caryclark@google.com235f56a2012-09-14 14:19:30 +00004402 coincidenceCheck(contourList);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004403 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004404 // construct closed contours
caryclark@google.com235f56a2012-09-14 14:19:30 +00004405 if (builder.xorMask() == kWinding_Mask) {
caryclark@google.com24bec792012-08-20 12:43:57 +00004406 bridgeWinding(contourList, simple);
4407 } else {
4408 bridgeXor(contourList, simple);
4409 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004410}
4411