blob: 9f8176b2884d71fd2e3f6c6e04c848e24b3e661e [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
caryclark@google.com47580692012-07-23 12:14:49 +000020// An Edge is a Segment generated from a Span
caryclark@google.com15fa1382012-05-07 20:49:36 +000021
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022// FIXME: remove once debugging is complete
caryclark@google.com47580692012-07-23 12:14:49 +000023#ifdef SK_DEBUG
24int gDebugMaxWindSum = SK_MaxS32;
25int gDebugMaxWindValue = SK_MaxS32;
26#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +000027
caryclark@google.comf839c032012-10-26 21:03:50 +000028#define PIN_ADD_T 0
caryclark@google.com0b7da432012-10-31 19:00:20 +000029#define TRY_ROTATE 1
caryclark@google.coma461ff02012-10-11 12:54:23 +000030
caryclark@google.com47580692012-07-23 12:14:49 +000031#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.com4eeda372012-12-06 21:47:48 +000032#define FORCE_RELEASE 0 // set force release to 1 for multiple thread -- no debugging
caryclark@google.comfa0588f2012-04-26 21:01:06 +000033
caryclark@google.com31143cf2012-11-09 22:14:19 +000034#if FORCE_RELEASE || defined SK_RELEASE
caryclark@google.com47580692012-07-23 12:14:49 +000035
36const bool gRunTestsInOneThread = false;
37
38#define DEBUG_ACTIVE_SPANS 0
caryclark@google.com4eeda372012-12-06 21:47:48 +000039#define DEBUG_ACTIVE_SPANS_SHORT_FORM 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000040#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000041#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000042#define DEBUG_ANGLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000043#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000044#define DEBUG_CROSS 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000045#define DEBUG_MARK_DONE 0
caryclark@google.comf839c032012-10-26 21:03:50 +000046#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com729e1c42012-11-21 21:36:34 +000047#define DEBUG_SHOW_WINDING 0
caryclark@google.com47580692012-07-23 12:14:49 +000048#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000049#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000050#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000051
52#else
53
caryclark@google.com47580692012-07-23 12:14:49 +000054const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000055
caryclark@google.comc91dfe42012-10-16 12:06:27 +000056#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com4eeda372012-12-06 21:47:48 +000057#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000058#define DEBUG_ADD_INTERSECTING_TS 1
59#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000060#define DEBUG_ANGLE 1
61#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000062#define DEBUG_CROSS 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000063#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000064#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com729e1c42012-11-21 21:36:34 +000065#define DEBUG_SHOW_WINDING 0
caryclark@google.com47580692012-07-23 12:14:49 +000066#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000067#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000068#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000069
70#endif
71
caryclark@google.com6aea33f2012-10-09 14:11:58 +000072#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000073
caryclark@google.comfa0588f2012-04-26 21:01:06 +000074#if DEBUG_DUMP
75static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000076// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000077static int gContourID;
78static int gSegmentID;
79#endif
80
caryclark@google.com8dcf1142012-07-02 20:27:02 +000081#ifndef DEBUG_TEST
82#define DEBUG_TEST 0
83#endif
84
caryclark@google.com32546db2012-08-31 20:55:07 +000085#define MAKE_CONST_LINE(line, pts) \
86 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
87#define MAKE_CONST_QUAD(quad, pts) \
88 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
89 {pts[2].fX, pts[2].fY}}
90#define MAKE_CONST_CUBIC(cubic, pts) \
91 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
92 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
93
caryclark@google.comfa0588f2012-04-26 21:01:06 +000094static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
95 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000096 MAKE_CONST_LINE(aLine, a);
97 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +000098 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
99}
100
101static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
102 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000103 MAKE_CONST_QUAD(aQuad, a);
104 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000105 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000106}
107
caryclark@google.com32546db2012-08-31 20:55:07 +0000108static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000109 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000110 MAKE_CONST_CUBIC(aCubic, a);
111 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000112 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
113}
114
115static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
116 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000117 MAKE_CONST_QUAD(aQuad, a);
118 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000119#define TRY_QUARTIC_SOLUTION 1
120#if TRY_QUARTIC_SOLUTION
121 intersect2(aQuad, bQuad, intersections);
122#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000123 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000124#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000125 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000126}
127
128static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
129 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000130 MAKE_CONST_CUBIC(aCubic, a);
131 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000132 intersect(aCubic, bCubic, intersections);
133 return intersections.fUsed;
134}
135
136static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
137 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000138 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000139 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
140}
141
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000142static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
143 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000144 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000145 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
146}
147
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000148static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
149 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000150 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000151 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
152}
153
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000154static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
155 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000156 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000157 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
158}
159
160static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
161 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000162 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000163 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
164}
165
166static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
167 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000168 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000169 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000170}
171
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000172static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
173 SkScalar , SkScalar , bool , Intersections& ) = {
174 NULL,
175 VLineIntersect,
176 VQuadIntersect,
177 VCubicIntersect
178};
179
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000180static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000181 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000182 double x, y;
183 xy_at_t(line, t, x, y);
184 out->fX = SkDoubleToScalar(x);
185 out->fY = SkDoubleToScalar(y);
186}
187
188static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000189 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000190 double x, y;
191 xy_at_t(quad, t, x, y);
192 out->fX = SkDoubleToScalar(x);
193 out->fY = SkDoubleToScalar(y);
194}
195
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000196static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
197 MAKE_CONST_QUAD(quad, a);
198 xy_at_t(quad, t, out->x, out->y);
199}
200
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000201static 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.com6aea33f2012-10-09 14:11:58 +0000463#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000464static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
465 Intersections& intersections) {
466 MAKE_CONST_QUAD(aQuad, a);
467 MAKE_CONST_LINE(bLine, b);
468 return intersectRay(aQuad, bLine, intersections);
469}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000470#endif
471
472static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
473 Intersections& intersections) {
474 MAKE_CONST_QUAD(aQuad, a);
475 return intersectRay(aQuad, bLine, intersections);
476}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000477
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000478class Segment;
479
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000480struct Span {
481 Segment* fOther;
482 mutable SkPoint fPt; // lazily computed as needed
483 double fT;
484 double fOtherT; // value at fOther[fOtherIndex].fT
485 int fOtherIndex; // can't be used during intersection
caryclark@google.com31143cf2012-11-09 22:14:19 +0000486 int fWindSum; // accumulated from contours surrounding this one.
487 int fOppSum; // for binary operators: the opposite winding sum
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000488 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000489 int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000490 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000491 bool fUnsortableStart; // set when start is part of an unsortable pair
492 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.comf839c032012-10-26 21:03:50 +0000493 bool fTiny; // if set, span may still be considered once for edge following
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000494};
495
caryclark@google.com15fa1382012-05-07 20:49:36 +0000496// sorting angles
497// given angles of {dx dy ddx ddy dddx dddy} sort them
498class Angle {
499public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000500 // FIXME: this is bogus for quads and cubics
501 // if the quads and cubics' line from end pt to ctrl pt are coincident,
502 // there's no obvious way to determine the curve ordering from the
503 // derivatives alone. In particular, if one quadratic's coincident tangent
504 // is longer than the other curve, the final control point can place the
505 // longer curve on either side of the shorter one.
506 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
507 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000508
caryclark@google.com32546db2012-08-31 20:55:07 +0000509 /*(
510 for quads and cubics, set up a parameterized line (e.g. LineParameters )
511 for points [0] to [1]. See if point [2] is on that line, or on one side
512 or the other. If it both quads' end points are on the same side, choose
513 the shorter tangent. If the tangents are equal, choose the better second
514 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000515
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000516 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000517 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000518 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000519 double y = dy();
520 double ry = rh.dy();
521 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
522 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000523 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000524 double x = dx();
525 double rx = rh.dx();
526 if (y == 0 && ry == 0 && x * rx < 0) {
527 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000528 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000529 double x_ry = x * ry;
530 double rx_y = rx * y;
531 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000532 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000533 return cmp < 0;
534 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000535 if (approximately_zero(x_ry) && approximately_zero(rx_y)
536 && !approximately_zero_squared(cmp)) {
537 return cmp < 0;
538 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000539 // at this point, the initial tangent line is coincident
caryclark@google.com31143cf2012-11-09 22:14:19 +0000540 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
541 || !approximately_zero(rh.fSide))) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000542 // FIXME: running demo will trigger this assertion
543 // (don't know if commenting out will trigger further assertion or not)
544 // commenting it out allows demo to run in release, though
545 // SkASSERT(fSide != rh.fSide);
546 return fSide < rh.fSide;
547 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000548 // see if either curve can be lengthened and try the tangent compare again
549 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
550 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
551 Angle longer = *this;
552 Angle rhLonger = rh;
553 if (longer.lengthen() | rhLonger.lengthen()) {
554 return longer < rhLonger;
555 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000556 // what if we extend in the other direction?
557 longer = *this;
558 rhLonger = rh;
559 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
560 return longer < rhLonger;
561 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000562 }
caryclark@google.com185c7c42012-10-19 18:26:24 +0000563 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
caryclark@google.com31143cf2012-11-09 22:14:19 +0000564 || (rh.fVerb == SkPath::kLine_Verb
565 && approximately_zero(rx) && approximately_zero(ry))) {
caryclark@google.com185c7c42012-10-19 18:26:24 +0000566 // See general unsortable comment below. This case can happen when
567 // one line has a non-zero change in t but no change in x and y.
568 fUnsortable = true;
569 rh.fUnsortable = true;
570 return this < &rh; // even with no solution, return a stable sort
571 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000572 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
573 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000574 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000575 // end of the shorter tangent to midway between the end points
576 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000577 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
578 double len = fTangent1.normalSquared();
579 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000580 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000581 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000582 int roots, rroots;
583 bool flip = false;
584 do {
585 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
586 double midX = (q[0].x + q[2].x) / 2;
587 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000588 ray[0] = q[1];
589 ray[1].x = midX;
590 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000591 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.comc91dfe42012-10-16 12:06:27 +0000595 if (roots == 0 || rroots == 0) {
596 // FIXME: we don't have a solution in this case. The interim solution
597 // is to mark the edges as unsortable, exclude them from this and
598 // future computations, and allow the returned path to be fragmented
599 fUnsortable = true;
600 rh.fUnsortable = true;
601 return this < &rh; // even with no solution, return a stable sort
602 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000603 _Point loc;
604 double best = SK_ScalarInfinity;
605 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000606 int index;
607 for (index = 0; index < roots; ++index) {
608 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000609 dx = loc.x - ray[0].x;
610 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000611 dist = dx * dx + dy * dy;
612 if (best > dist) {
613 best = dist;
614 }
615 }
616 for (index = 0; index < rroots; ++index) {
617 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000618 dx = loc.x - ray[0].x;
619 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000620 dist = dx * dx + dy * dy;
621 if (best > dist) {
622 return fSide < 0;
623 }
624 }
625 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000626 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000627
caryclark@google.com47580692012-07-23 12:14:49 +0000628 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000629 return fTangent1.dx();
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 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000634 }
635
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000636 int end() const {
637 return fEnd;
638 }
639
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000640 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000641 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000642 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000643
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000644 bool lengthen() {
645 int newEnd = fEnd;
646 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
647 fEnd = newEnd;
648 setSpans();
649 return true;
650 }
651 return false;
652 }
653
caryclark@google.coma461ff02012-10-11 12:54:23 +0000654 bool reverseLengthen() {
655 if (fReversed) {
656 return false;
657 }
658 int newEnd = fStart;
659 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
660 fEnd = newEnd;
661 fReversed = true;
662 setSpans();
663 return true;
664 }
665 return false;
666 }
667
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000668 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000669 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000670 fSegment = segment;
671 fStart = start;
672 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000673 fPts = orig;
674 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000675 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000676 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000677 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000678 setSpans();
679 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000680
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000681 void setSpans() {
682 double startT = (*fSpans)[fStart].fT;
683 double endT = (*fSpans)[fEnd].fT;
684 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000685 case SkPath::kLine_Verb:
686 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000687 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000688 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
689 fTangent1.lineEndPoints(l);
caryclark@google.comf839c032012-10-26 21:03:50 +0000690 fUnsortable = dx() == 0 && dy() == 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000691 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000692 break;
693 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000694 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000695 fTangent1.quadEndPoints(fQ, 0, 1);
696 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000697 break;
698 case SkPath::kCubic_Verb:
699 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000700 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000701 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000702 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000703 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000704 default:
705 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000706 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000707 if (fUnsortable) {
708 return;
709 }
710 SkASSERT(fStart != fEnd);
711 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
712 for (int index = fStart; index != fEnd; index += step) {
713 if ((*fSpans)[index].fUnsortableStart) {
714 fUnsortable = true;
715 return;
716 }
717 if (index != fStart && (*fSpans)[index].fUnsortableEnd) {
718 fUnsortable = true;
719 return;
720 }
721 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000722 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000723
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000724 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000725 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000726 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000727
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000728 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000729 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000730 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000731
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000732 const SkTDArray<Span>* spans() const {
733 return fSpans;
734 }
735
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000736 int start() const {
737 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000738 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000739
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000740 bool unsortable() const {
741 return fUnsortable;
742 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000743
caryclark@google.comc899ad92012-08-23 15:24:42 +0000744#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000745 const SkPoint* pts() const {
746 return fPts;
747 }
748
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000749 SkPath::Verb verb() const {
750 return fVerb;
751 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000752
caryclark@google.comc899ad92012-08-23 15:24:42 +0000753 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000754 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000755 }
756#endif
757
caryclark@google.com15fa1382012-05-07 20:49:36 +0000758private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000759 const SkPoint* fPts;
760 Quadratic fQ;
761 SkPath::Verb fVerb;
762 double fSide;
763 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000764 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000765 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000766 int fStart;
767 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000768 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000769 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000770};
771
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000772// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000773struct Bounds : public SkRect {
774 static bool Intersects(const Bounds& a, const Bounds& b) {
775 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
776 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
777 }
778
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000779 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
780 if (left < fLeft) {
781 fLeft = left;
782 }
783 if (top < fTop) {
784 fTop = top;
785 }
786 if (right > fRight) {
787 fRight = right;
788 }
789 if (bottom > fBottom) {
790 fBottom = bottom;
791 }
792 }
793
794 void add(const Bounds& toAdd) {
795 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
796 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000797
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000798 bool isEmpty() {
799 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000800 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000801 || isnan(fLeft) || isnan(fRight)
802 || isnan(fTop) || isnan(fBottom);
803 }
804
805 void setCubicBounds(const SkPoint a[4]) {
806 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000807 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000808 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000809 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
810 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000811 }
812
813 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000814 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000815 _Rect dRect;
816 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000817 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
818 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000819 }
820};
821
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000822// OPTIMIZATION: does the following also work, and is it any faster?
823// return outerWinding * innerWinding > 0
824// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
caryclark@google.com2ddff932012-08-07 21:25:27 +0000825static bool useInnerWinding(int outerWinding, int innerWinding) {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000826 // SkASSERT(outerWinding != innerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +0000827 int absOut = abs(outerWinding);
828 int absIn = abs(innerWinding);
829 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
830 if (outerWinding * innerWinding < 0) {
831#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000832 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000833 outerWinding, innerWinding, result ? "true" : "false");
834#endif
835 }
836 return result;
837}
838
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000839#define F (false) // discard the edge
840#define T (true) // keep the edge
841
842static const bool gActiveEdge[kShapeOp_Count][2][2][2][2] = {
843// miFrom=0 miFrom=1
844// miTo=0 miTo=1 miTo=0 miTo=1
845// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
846// suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1
847 {{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su
848 {{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su
849 {{{{F, T}, {T, F}}, {{T, T}, {F, F}}}, {{{T, F}, {T, F}}, {{F, F}, {F, F}}}}, // mi | su
850 {{{{F, T}, {T, F}}, {{T, F}, {F, T}}}, {{{T, F}, {F, T}}, {{F, T}, {T, F}}}}, // mi ^ su
caryclark@google.com235f56a2012-09-14 14:19:30 +0000851};
852
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000853#undef F
854#undef T
caryclark@google.com235f56a2012-09-14 14:19:30 +0000855
caryclark@google.comf839c032012-10-26 21:03:50 +0000856// wrap path to keep track of whether the contour is initialized and non-empty
857class PathWrapper {
858public:
859 PathWrapper(SkPath& path)
860 : fPathPtr(&path)
861 {
862 init();
863 }
864
865 void close() {
866 if (!fHasMove) {
867 return;
868 }
869 bool callClose = isClosed();
870 lineTo();
871 if (fEmpty) {
872 return;
873 }
874 if (callClose) {
875 #if DEBUG_PATH_CONSTRUCTION
876 SkDebugf("path.close();\n");
877 #endif
878 fPathPtr->close();
879 }
880 init();
881 }
882
883 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
884 lineTo();
885 moveTo();
886#if DEBUG_PATH_CONSTRUCTION
887 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
888 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
889#endif
890 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
891 fDefer[0] = fDefer[1] = pt3;
892 fEmpty = false;
893 }
894
895 void deferredLine(const SkPoint& pt) {
896 if (pt == fDefer[1]) {
897 return;
898 }
899 if (changedSlopes(pt)) {
900 lineTo();
901 fDefer[0] = fDefer[1];
902 }
903 fDefer[1] = pt;
904 }
905
906 void deferredMove(const SkPoint& pt) {
907 fMoved = true;
908 fHasMove = true;
909 fEmpty = true;
910 fDefer[0] = fDefer[1] = pt;
911 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000912
caryclark@google.comf839c032012-10-26 21:03:50 +0000913 void deferredMoveLine(const SkPoint& pt) {
914 if (!fHasMove) {
915 deferredMove(pt);
916 }
917 deferredLine(pt);
918 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000919
caryclark@google.comf839c032012-10-26 21:03:50 +0000920 bool hasMove() const {
921 return fHasMove;
922 }
923
924 void init() {
925 fEmpty = true;
926 fHasMove = false;
927 fMoved = false;
928 }
929
930 bool isClosed() const {
931 return !fEmpty && fFirstPt == fDefer[1];
932 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000933
caryclark@google.comf839c032012-10-26 21:03:50 +0000934 void lineTo() {
935 if (fDefer[0] == fDefer[1]) {
936 return;
937 }
938 moveTo();
939 fEmpty = false;
940#if DEBUG_PATH_CONSTRUCTION
941 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
942#endif
943 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
944 fDefer[0] = fDefer[1];
945 }
946
947 const SkPath* nativePath() const {
948 return fPathPtr;
949 }
950
951 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
952 lineTo();
953 moveTo();
954#if DEBUG_PATH_CONSTRUCTION
955 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
956 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
957#endif
958 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
959 fDefer[0] = fDefer[1] = pt2;
960 fEmpty = false;
961 }
962
963protected:
964 bool changedSlopes(const SkPoint& pt) const {
965 if (fDefer[0] == fDefer[1]) {
966 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000967 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000968 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
969 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
970 SkScalar lineDx = pt.fX - fDefer[1].fX;
971 SkScalar lineDy = pt.fY - fDefer[1].fY;
972 return deferDx * lineDy != deferDy * lineDx;
973 }
974
975 void moveTo() {
976 if (!fMoved) {
977 return;
978 }
979 fFirstPt = fDefer[0];
980#if DEBUG_PATH_CONSTRUCTION
981 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
982#endif
983 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
984 fMoved = false;
985 }
986
987private:
988 SkPath* fPathPtr;
989 SkPoint fDefer[2];
990 SkPoint fFirstPt;
991 bool fEmpty;
992 bool fHasMove;
993 bool fMoved;
994};
995
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000996class Segment {
997public:
998 Segment() {
999#if DEBUG_DUMP
1000 fID = ++gSegmentID;
1001#endif
1002 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001003
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001004 bool operator<(const Segment& rh) const {
1005 return fBounds.fTop < rh.fBounds.fTop;
1006 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001007
caryclark@google.com4eeda372012-12-06 21:47:48 +00001008 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001009 if (activeAngleInner(index, done, angles)) {
1010 return true;
1011 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001012 double referenceT = fTs[index].fT;
1013 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001014 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001015 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001016 return true;
1017 }
1018 }
1019 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001020 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001021 return true;
1022 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001023 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001024 return false;
1025 }
1026
caryclark@google.com4eeda372012-12-06 21:47:48 +00001027 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001028 Span* span = &fTs[index];
1029 Segment* other = span->fOther;
1030 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001031 return other->activeAngleInner(oIndex, done, angles);
1032 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001033
caryclark@google.com4eeda372012-12-06 21:47:48 +00001034 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001035 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001036 if (next > 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001037 Span& upSpan = fTs[index];
1038 if (upSpan.fWindValue || upSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001039 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001040 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001041 done++;
1042 } else if (upSpan.fWindSum != SK_MinS32) {
1043 return true;
1044 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001045 } else if (!upSpan.fDone) {
1046 upSpan.fDone = true;
1047 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001048 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001049 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001050 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001051 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001052 if (prev >= 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001053 Span& downSpan = fTs[prev];
1054 if (downSpan.fWindValue || downSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001055 addAngle(angles, index, prev);
1056 if (downSpan.fDone) {
1057 done++;
1058 } else if (downSpan.fWindSum != SK_MinS32) {
1059 return true;
1060 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001061 } else if (!downSpan.fDone) {
1062 downSpan.fDone = true;
1063 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001064 }
1065 }
1066 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001067 }
1068
caryclark@google.comf839c032012-10-26 21:03:50 +00001069 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001070 SkASSERT(!done());
1071 int count = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001072 result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001073 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001074 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001075 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001076 const Span& span = fTs[index];
1077 if (span.fUnsortableStart | lastUnsortable) {
1078 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001079 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001080 if (!span.fDone | !lastDone) {
1081 const SkPoint& xy = xyAtT(index);
1082 if (result.fY < xy.fY) {
1083 goto next;
1084 }
1085 if (result.fY == xy.fY && result.fX < xy.fX) {
1086 goto next;
1087 }
1088 result = xy;
1089 }
1090 next:
1091 lastDone = span.fDone;
1092 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001093 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001094 SkASSERT(result.fY < SK_ScalarMax);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001095 }
1096
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001097 bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, ShapeOp op) {
1098 int sumMiWinding = updateWinding(endIndex, index);
1099 int sumSuWinding = updateOppWinding(endIndex, index);
1100 if (fOperand) {
1101 SkTSwap<int>(sumMiWinding, sumSuWinding);
1102 }
1103 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
1104 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, sumMiWinding, sumSuWinding,
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001105 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001106 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001107
1108 bool activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, ShapeOp op,
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001109 int& sumMiWinding, int& sumSuWinding,
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001110 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
1111 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
1112 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001113 bool miFrom;
1114 bool miTo;
1115 bool suFrom;
1116 bool suTo;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001117 if (operand()) {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001118 miFrom = (oppMaxWinding & xorMiMask) != 0;
1119 miTo = (oppSumWinding & xorMiMask) != 0;
1120 suFrom = (maxWinding & xorSuMask) != 0;
1121 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001122 } else {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001123 miFrom = (maxWinding & xorMiMask) != 0;
1124 miTo = (sumWinding & xorMiMask) != 0;
1125 suFrom = (oppMaxWinding & xorSuMask) != 0;
1126 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001127 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001128 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
1129 SkASSERT(result != -1);
1130 return result;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001131 }
1132
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001133 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001134 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001135 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001136#if DEBUG_ANGLE
caryclark@google.com31143cf2012-11-09 22:14:19 +00001137 if (angles.count() > 1 && !fTs[start].fTiny) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001138 SkPoint angle0Pt, newPt;
1139 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1140 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1141 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1142 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1143 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1144 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001145#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001146 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001147 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001148
caryclark@google.com2ddff932012-08-07 21:25:27 +00001149 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001150 double oEnd) {
1151 int tIndex = -1;
1152 int tCount = fTs.count();
1153 int oIndex = -1;
1154 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001155 do {
1156 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001157 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001158 int tIndexStart = tIndex;
1159 do {
1160 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001161 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001162 int oIndexStart = oIndex;
1163 double nextT;
1164 do {
1165 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001166 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001167 double oNextT;
1168 do {
1169 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001170 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001171 // at this point, spans before and after are at:
1172 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1173 // if tIndexStart == 0, no prior span
1174 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001175
caryclark@google.comcc905052012-07-25 20:59:42 +00001176 // advance the span with zero winding
1177 // if the following span exists (not past the end, non-zero winding)
1178 // connect the two edges
1179 if (!fTs[tIndexStart].fWindValue) {
1180 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1181 #if DEBUG_CONCIDENT
1182 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1183 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001184 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1185 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001186 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001187 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001188 }
1189 if (nextT < 1 && fTs[tIndex].fWindValue) {
1190 #if DEBUG_CONCIDENT
1191 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1192 __FUNCTION__, fID, other.fID, tIndex,
1193 fTs[tIndex].fT, xyAtT(tIndex).fX,
1194 xyAtT(tIndex).fY);
1195 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001196 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001197 }
1198 } else {
1199 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1200 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1201 #if DEBUG_CONCIDENT
1202 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1203 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001204 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1205 other.xyAtT(oIndexStart).fY);
1206 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001207 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001208 }
1209 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1210 #if DEBUG_CONCIDENT
1211 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1212 __FUNCTION__, fID, other.fID, oIndex,
1213 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1214 other.xyAtT(oIndex).fY);
1215 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1216 #endif
1217 }
1218 }
1219 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001220
caryclark@google.comcc905052012-07-25 20:59:42 +00001221 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1222 double oEnd) {
1223 // walk this to outsideTs[0]
1224 // walk other to outsideTs[1]
1225 // if either is > 0, add a pointer to the other, copying adjacent winding
1226 int tIndex = -1;
1227 int oIndex = -1;
1228 double tStart = outsideTs[0];
1229 double oStart = outsideTs[1];
1230 do {
1231 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001232 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001233 do {
1234 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001235 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001236 if (tIndex > 0 || oIndex > 0 || fOperand != other.fOperand) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001237 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001238 }
1239 tStart = fTs[tIndex].fT;
1240 oStart = other.fTs[oIndex].fT;
1241 do {
1242 double nextT;
1243 do {
1244 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001245 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001246 tStart = nextT;
1247 do {
1248 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001249 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001250 oStart = nextT;
caryclark@google.com4eeda372012-12-06 21:47:48 +00001251 if (tStart == 1 && oStart == 1 && fOperand == other.fOperand) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001252 break;
1253 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001254 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001255 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001256 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001257
caryclark@google.com4eeda372012-12-06 21:47:48 +00001258 void addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
1259 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001260 fBounds.setCubicBounds(pts);
1261 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001262
caryclark@google.comf839c032012-10-26 21:03:50 +00001263 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001264 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001265 const SkPoint* ePtr;
1266 int lastT = fTs.count() - 1;
1267 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1268 ePtr = fPts;
1269 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001270 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001271 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1272 ePtr = edge;
1273 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001274 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001275 bool reverse = ePtr == fPts && start != 0;
1276 if (reverse) {
1277 path.deferredMoveLine(ePtr[fVerb]);
1278 switch (fVerb) {
1279 case SkPath::kLine_Verb:
1280 path.deferredLine(ePtr[0]);
1281 break;
1282 case SkPath::kQuad_Verb:
1283 path.quadTo(ePtr[1], ePtr[0]);
1284 break;
1285 case SkPath::kCubic_Verb:
1286 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1287 break;
1288 default:
1289 SkASSERT(0);
1290 }
1291 // return ePtr[0];
1292 } else {
1293 path.deferredMoveLine(ePtr[0]);
1294 switch (fVerb) {
1295 case SkPath::kLine_Verb:
1296 path.deferredLine(ePtr[1]);
1297 break;
1298 case SkPath::kQuad_Verb:
1299 path.quadTo(ePtr[1], ePtr[2]);
1300 break;
1301 case SkPath::kCubic_Verb:
1302 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1303 break;
1304 default:
1305 SkASSERT(0);
1306 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001307 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001308 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001309 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001310 }
1311
caryclark@google.com4eeda372012-12-06 21:47:48 +00001312 void addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
1313 init(pts, SkPath::kLine_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001314 fBounds.set(pts, 2);
1315 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001316
caryclark@google.comf839c032012-10-26 21:03:50 +00001317#if 0
1318 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001319 const SkPoint& pt = xyAtT(tIndex);
1320 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001321 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001322 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001323 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001324 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001325#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001326
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001327 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001328 void addOtherT(int index, double otherT, int otherIndex) {
1329 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001330 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001331 if (precisely_less_than_zero(otherT)) {
1332 otherT = 0;
1333 } else if (precisely_greater_than_one(otherT)) {
1334 otherT = 1;
1335 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001336 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001337 span.fOtherT = otherT;
1338 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001339 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001340
caryclark@google.com4eeda372012-12-06 21:47:48 +00001341 void addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
1342 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001343 fBounds.setQuadBounds(pts);
1344 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001345
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001346 // Defer all coincident edge processing until
1347 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001348
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001349// no need to be tricky; insert in normal T order
1350// resolve overlapping ts when considering coincidence later
1351
1352 // add non-coincident intersection. Resulting edges are sorted in T.
1353 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001354 // FIXME: in the pathological case where there is a ton of intercepts,
1355 // binary search?
1356 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001357 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001358 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001359 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001360 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001361 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001362 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001363 newT = 1;
1364 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001365 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001366 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001367 // OPTIMIZATION: if there are three or more identical Ts, then
1368 // the fourth and following could be further insertion-sorted so
1369 // that all the edges are clockwise or counterclockwise.
1370 // This could later limit segment tests to the two adjacent
1371 // neighbors, although it doesn't help with determining which
1372 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001373 if (newT < fTs[index].fT) {
1374 insertedAt = index;
1375 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001376 }
1377 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001378 Span* span;
1379 if (insertedAt >= 0) {
1380 span = fTs.insert(insertedAt);
1381 } else {
1382 insertedAt = tCount;
1383 span = fTs.append();
1384 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001385 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001386 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001387 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001388 span->fWindSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001389 span->fOppSum = SK_MinS32;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001390 span->fWindValue = 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001391 span->fOppValue = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001392 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001393 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001394 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001395 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001396 span->fUnsortableStart = false;
1397 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001398 if (span - fTs.begin() > 0 && !span[-1].fDone
1399 && !precisely_negative(newT - span[-1].fT)
1400 // && approximately_negative(newT - span[-1].fT)
1401 && xyAtT(&span[-1]) == xyAtT(span)) {
1402 span[-1].fTiny = true;
1403 span[-1].fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001404 if (approximately_negative(newT - span[-1].fT)) {
1405 if (approximately_greater_than_one(newT)) {
1406 span[-1].fUnsortableStart = true;
1407 span[-2].fUnsortableEnd = true;
1408 }
1409 if (approximately_less_than_zero(span[-1].fT)) {
1410 span->fUnsortableStart = true;
1411 span[-1].fUnsortableEnd = true;
1412 }
1413 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001414 ++fDoneSpans;
1415 }
1416 if (fTs.end() - span > 1 && !span->fDone
1417 && !precisely_negative(span[1].fT - newT)
1418 // && approximately_negative(span[1].fT - newT)
1419 && xyAtT(&span[1]) == xyAtT(span)) {
1420 span->fTiny = true;
1421 span->fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001422 if (approximately_negative(span[1].fT - newT)) {
1423 if (approximately_greater_than_one(span[1].fT)) {
1424 span->fUnsortableStart = true;
1425 span[-1].fUnsortableEnd = true;
1426 }
1427 if (approximately_less_than_zero(newT)) {
1428 span[1].fUnsortableStart = true;
1429 span->fUnsortableEnd = true;
1430 }
1431 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001432 ++fDoneSpans;
1433 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001434 return insertedAt;
1435 }
1436
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001437 // set spans from start to end to decrement by one
1438 // note this walks other backwards
1439 // FIMXE: there's probably an edge case that can be constructed where
1440 // two span in one segment are separated by float epsilon on one span but
1441 // not the other, if one segment is very small. For this
1442 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001443 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001444 // sorted? It feels better in such a case to match the span's other span
1445 // pointer since both coincident segments must contain the same spans.
1446 void addTCancel(double startT, double endT, Segment& other,
1447 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001448 SkASSERT(!approximately_negative(endT - startT));
1449 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001450 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001451 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001452 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001453 ++index;
1454 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001455 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001456 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001457 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001458 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001459 Span* test = &fTs[index];
1460 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001461 SkTDArray<double> outsideTs;
1462 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001463 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001464 bool decrement = test->fWindValue && oTest->fWindValue && !binary;
caryclark@google.comcc905052012-07-25 20:59:42 +00001465 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001466 double testT = test->fT;
1467 double oTestT = oTest->fT;
1468 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001469 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001470 if (decrement) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001471 decrementSpan(span);
caryclark@google.com200c2112012-08-03 15:05:04 +00001472 } else if (track && span->fT < 1 && oTestT < 1) {
1473 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001474 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001475 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001476 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001477 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001478 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1479 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1480 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001481 while (approximately_negative(otherTMatchStart - oSpan->fT)
1482 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001483 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001484 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001485 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001486 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001487 other.decrementSpan(oSpan);
1488 } else if (track && oSpan->fT < 1 && testT < 1) {
1489 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001490 }
1491 if (!oIndex) {
1492 break;
1493 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001494 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001495 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001496 test = span;
1497 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001498 } while (!approximately_negative(endT - test->fT));
1499 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001500 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001501 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001502 double tStart = outsideTs[0];
1503 double oStart = outsideTs[1];
1504 addCancelOutsides(tStart, oStart, other, oEndT);
1505 int count = outsideTs.count();
1506 if (count > 2) {
1507 double tStart = outsideTs[count - 2];
1508 double oStart = outsideTs[count - 1];
1509 addCancelOutsides(tStart, oStart, other, oEndT);
1510 }
caryclark@google.com18063442012-07-25 12:05:18 +00001511 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001512 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001513 double tStart = oOutsideTs[0];
1514 double oStart = oOutsideTs[1];
1515 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001516 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001517 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00001518
caryclark@google.com4eeda372012-12-06 21:47:48 +00001519 int bumpCoincidentThis(const Span* oTest, bool opp, int index,
1520 SkTDArray<double>& outsideTs) {
1521 int oWindValue = oTest->fWindValue;
1522 int oOppValue = oTest->fOppValue;
1523 if (opp) {
1524 SkTSwap<int>(oWindValue, oOppValue);
1525 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001526 Span* const test = &fTs[index];
1527 Span* end = test;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001528 const double oStartT = oTest->fT;
1529 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001530 if (bumpSpan(end, oWindValue, oOppValue)) {
1531 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001532 }
1533 end = &fTs[++index];
1534 } while (approximately_negative(end->fT - test->fT));
1535 return index;
1536 }
1537
1538 // because of the order in which coincidences are resolved, this and other
1539 // may not have the same intermediate points. Compute the corresponding
1540 // intermediate T values (using this as the master, other as the follower)
1541 // and walk other conditionally -- hoping that it catches up in the end
caryclark@google.com4eeda372012-12-06 21:47:48 +00001542 int bumpCoincidentOther(const Span* test, double oEndT, int& oIndex,
1543 SkTDArray<double>& oOutsideTs) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001544 Span* const oTest = &fTs[oIndex];
1545 Span* oEnd = oTest;
1546 const double startT = test->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001547 const double oStartT = oTest->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001548 while (!approximately_negative(oEndT - oEnd->fT)
caryclark@google.com4eeda372012-12-06 21:47:48 +00001549 && approximately_negative(oEnd->fT - oStartT)) {
1550 zeroSpan(oEnd);
1551 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001552 oEnd = &fTs[++oIndex];
1553 }
1554 return oIndex;
1555 }
1556
1557 // FIXME: need to test this case:
1558 // contourA has two segments that are coincident
1559 // contourB has two segments that are coincident in the same place
1560 // each ends up with +2/0 pairs for winding count
1561 // since logic below doesn't transfer count (only increments/decrements) can this be
1562 // resolved to +4/0 ?
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001563
1564 // set spans from start to end to increment the greater by one and decrement
1565 // the lesser
caryclark@google.com4eeda372012-12-06 21:47:48 +00001566 void addTCoincident(double startT, double endT, Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001567 SkASSERT(!approximately_negative(endT - startT));
1568 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001569 bool opp = fOperand ^ other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001570 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001571 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001572 ++index;
1573 }
1574 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001575 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001576 ++oIndex;
1577 }
1578 Span* test = &fTs[index];
1579 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001580 SkTDArray<double> outsideTs;
1581 SkTDArray<double> oOutsideTs;
1582 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001583 // if either span has an opposite value and the operands don't match, resolve first
1584 index = bumpCoincidentThis(oTest, opp, index, outsideTs);
1585 oIndex = other.bumpCoincidentOther(test, oEndT, oIndex, oOutsideTs);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001586 test = &fTs[index];
1587 oTest = &other.fTs[oIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001588 } while (!approximately_negative(endT - test->fT));
1589 SkASSERT(approximately_negative(oTest->fT - oEndT));
1590 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001591 if (!done() && outsideTs.count()) {
1592 addCoinOutsides(outsideTs, other, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001593 }
1594 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001595 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001596 }
1597 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001598
caryclark@google.comcc905052012-07-25 20:59:42 +00001599 // FIXME: this doesn't prevent the same span from being added twice
1600 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001601 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001602 int tCount = fTs.count();
1603 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1604 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001605 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001606 break;
1607 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001608 if (approximately_negative(span.fT - t) && span.fOther == &other
1609 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001610#if DEBUG_ADD_T_PAIR
1611 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1612 __FUNCTION__, fID, t, other.fID, otherT);
1613#endif
1614 return;
1615 }
1616 }
caryclark@google.com47580692012-07-23 12:14:49 +00001617#if DEBUG_ADD_T_PAIR
1618 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1619 __FUNCTION__, fID, t, other.fID, otherT);
1620#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001621 int insertedAt = addT(t, &other);
1622 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001623 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001624 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001625 matchWindingValue(insertedAt, t, borrowWind);
1626 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001627 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001628
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001629 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001630 // add edge leading into junction
caryclark@google.com4eeda372012-12-06 21:47:48 +00001631 int min = SkMin32(end, start);
1632 if (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001633 addAngle(angles, end, start);
1634 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001635 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001636 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001637 int tIndex = nextExactSpan(end, step);
caryclark@google.com4eeda372012-12-06 21:47:48 +00001638 min = SkMin32(end, tIndex);
1639 if (tIndex >= 0 && (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001640 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001641 }
1642 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001643
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001644 const Bounds& bounds() const {
1645 return fBounds;
1646 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001647
caryclark@google.com31143cf2012-11-09 22:14:19 +00001648 void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001649 double referenceT = fTs[index].fT;
1650 int lesser = index;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001651 while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand)
1652 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00001653 buildAnglesInner(lesser, angles);
1654 }
1655 do {
1656 buildAnglesInner(index, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001657 } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand)
1658 && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001659 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001660
1661 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1662 Span* span = &fTs[index];
1663 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001664 // if there is only one live crossing, and no coincidence, continue
1665 // in the same direction
1666 // if there is coincidence, the only choice may be to reverse direction
1667 // find edge on either side of intersection
1668 int oIndex = span->fOtherIndex;
1669 // if done == -1, prior span has already been processed
1670 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001671 int next = other->nextExactSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001672 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001673 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001674 next = other->nextExactSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001675 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001676 // add candidate into and away from junction
1677 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001678 }
1679
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001680 int computeSum(int startIndex, int endIndex, bool binary) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001681 SkTDArray<Angle> angles;
1682 addTwoAngles(startIndex, endIndex, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001683 buildAngles(endIndex, angles, false);
caryclark@google.comd1688742012-09-18 20:08:37 +00001684 // OPTIMIZATION: check all angles to see if any have computed wind sum
1685 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001686 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001687 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001688#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001689 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001690#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001691 if (!sortable) {
1692 return SK_MinS32;
1693 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001694 int angleCount = angles.count();
1695 const Angle* angle;
1696 const Segment* base;
1697 int winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001698 int oWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001699 int firstIndex = 0;
1700 do {
1701 angle = sorted[firstIndex];
1702 base = angle->segment();
1703 winding = base->windSum(angle);
1704 if (winding != SK_MinS32) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001705 oWinding = base->oppSum(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001706 break;
1707 }
1708 if (++firstIndex == angleCount) {
1709 return SK_MinS32;
1710 }
1711 } while (true);
1712 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001713 int spanWinding = base->spanSign(angle);
1714 bool inner = useInnerWinding(winding + spanWinding, winding);
1715 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001716 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001717 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001718 inner ? winding + spanWinding : winding);
1719 #endif
1720 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001721 winding += spanWinding;
1722 }
1723 #if DEBUG_SORT
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001724 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001725 #endif
1726 int nextIndex = firstIndex + 1;
1727 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001728 winding -= base->spanSign(angle);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001729 oWinding -= base->oppSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001730 do {
1731 if (nextIndex == angleCount) {
1732 nextIndex = 0;
1733 }
1734 angle = sorted[nextIndex];
1735 Segment* segment = angle->segment();
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001736 bool opp = base->fOperand ^ segment->fOperand;
1737 int maxWinding, oMaxWinding;
1738 int spanSign = segment->spanSign(angle);
1739 int oppoSign = segment->oppSign(angle);
1740 if (opp) {
1741 oMaxWinding = oWinding;
1742 oWinding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001743 maxWinding = winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001744 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001745 winding -= oppoSign;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001746 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001747 } else {
1748 maxWinding = winding;
1749 winding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001750 oMaxWinding = oWinding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001751 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001752 oWinding -= oppoSign;
1753 }
1754 }
1755 if (segment->windSum(angle) == SK_MinS32) {
1756 if (opp) {
1757 if (useInnerWinding(oMaxWinding, oWinding)) {
1758 oMaxWinding = oWinding;
1759 }
1760 if (oppoSign && useInnerWinding(maxWinding, winding)) {
1761 maxWinding = winding;
1762 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001763 (void) segment->markAndChaseWinding(angle, oMaxWinding, maxWinding);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001764 } else {
1765 if (useInnerWinding(maxWinding, winding)) {
1766 maxWinding = winding;
1767 }
1768 if (oppoSign && useInnerWinding(oMaxWinding, oWinding)) {
1769 oMaxWinding = oWinding;
1770 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001771 (void) segment->markAndChaseWinding(angle, maxWinding, binary ? oMaxWinding : 0);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001772 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001773 }
1774 } while (++nextIndex != lastIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001775 int minIndex = SkMin32(startIndex, endIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001776 return windSum(minIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001777 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001778
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001779 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001780 int bestT = -1;
1781 SkScalar top = bounds().fTop;
1782 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001783 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001784 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001785 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001786 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001787 if (fTs[start].fWindValue == 0) {
1788 continue;
1789 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001790 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001791 double startT = fTs[start].fT;
1792 double endT = fTs[end].fT;
1793 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001794 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001795 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001796 // FIXME: always use original and limit results to T values within
1797 // start t and end t.
1798 // OPTIMIZE: use specialty function that intersects ray with curve,
1799 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001800 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1801 false, intersections);
1802 if (pts == 0) {
1803 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001804 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001805 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1806 // if the intersection is edge on, wait for another one
1807 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001808 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001809 for (int index = 0; index < pts; ++index) {
1810 SkPoint pt;
1811 double foundT = intersections.fT[0][index];
1812 double testT = startT + (endT - startT) * foundT;
1813 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1814 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001815 if (fVerb > SkPath::kLine_Verb
1816 && !approximately_less_than_zero(foundT)
1817 && !approximately_greater_than_one(foundT)) {
1818 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1819 if (approximately_zero(dx)) {
1820 continue;
1821 }
1822 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001823 bestY = pt.fY;
1824 bestT = foundT < 1 ? start : end;
1825 hitT = testT;
1826 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001827 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001828 } while (fTs[end].fT != 1);
1829 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001830 }
caryclark@google.com18063442012-07-25 12:05:18 +00001831
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001832 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1833 // if a segment is connected to this one, consider it crossing
1834 int tIndex;
1835 if (fPts[0].fX == basePt.fX) {
1836 tIndex = 0;
1837 do {
1838 const Span& sSpan = fTs[tIndex];
1839 const Segment* sOther = sSpan.fOther;
1840 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1841 continue;
1842 }
1843 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1844 : sOther->fBounds.fRight > basePt.fX) {
1845 return true;
1846 }
1847 } while (fTs[++tIndex].fT == 0);
1848 }
1849 if (fPts[fVerb].fX == basePt.fX) {
1850 tIndex = fTs.count() - 1;
1851 do {
1852 const Span& eSpan = fTs[tIndex];
1853 const Segment* eOther = eSpan.fOther;
1854 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1855 continue;
1856 }
1857 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1858 : eOther->fBounds.fRight > basePt.fX) {
1859 return true;
1860 }
1861 } while (fTs[--tIndex].fT == 1);
1862 }
1863 return false;
1864 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001865
caryclark@google.com4eeda372012-12-06 21:47:48 +00001866 void decrementSpan(Span* span) {
caryclark@google.com18063442012-07-25 12:05:18 +00001867 SkASSERT(span->fWindValue > 0);
1868 if (--(span->fWindValue) == 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001869 if (!span->fOppValue && !span->fDone) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001870 span->fDone = true;
1871 ++fDoneSpans;
1872 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001873 }
1874 }
1875
1876 bool bumpSpan(Span* span, int windDelta, int oppDelta) {
1877 SkASSERT(!span->fDone);
1878 span->fWindValue += windDelta;
1879 SkASSERT(span->fWindValue >= 0);
1880 span->fOppValue += oppDelta;
1881 SkASSERT(span->fOppValue >= 0);
1882 if (fXor) {
1883 span->fWindValue &= 1;
1884 }
1885 if (fOppXor) {
1886 span->fOppValue &= 1;
1887 }
1888 if (!span->fWindValue && !span->fOppValue) {
1889 span->fDone = true;
1890 ++fDoneSpans;
caryclark@google.com18063442012-07-25 12:05:18 +00001891 return true;
1892 }
1893 return false;
1894 }
1895
caryclark@google.com15fa1382012-05-07 20:49:36 +00001896 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001897 SkASSERT(fDoneSpans <= fTs.count());
1898 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001899 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001900
caryclark@google.comf839c032012-10-26 21:03:50 +00001901 bool done(int min) const {
1902 return fTs[min].fDone;
1903 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001904
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001905 bool done(const Angle* angle) const {
1906 return done(SkMin32(angle->start(), angle->end()));
caryclark@google.com47580692012-07-23 12:14:49 +00001907 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001908
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001909 /*
1910 The M and S variable name parts stand for the operators.
1911 Mi stands for Minuend (see wiki subtraction, analogous to difference)
1912 Su stands for Subtrahend
1913 The Opp variable name part designates that the value is for the Opposite operator.
1914 Opposite values result from combining coincident spans.
1915 */
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001916
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001917 Segment* findNextOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
1918 bool& unsortable, ShapeOp op, const int xorMiMask, const int xorSuMask) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001919 const int startIndex = nextStart;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001920 const int endIndex = nextEnd;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001921 SkASSERT(startIndex != endIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001922 const int count = fTs.count();
1923 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
1924 const int step = SkSign32(endIndex - startIndex);
1925 const int end = nextExactSpan(startIndex, step);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001926 SkASSERT(end >= 0);
1927 Span* endSpan = &fTs[end];
1928 Segment* other;
1929 if (isSimple(end)) {
1930 // mark the smaller of startIndex, endIndex done, and all adjacent
1931 // spans with the same T value (but not 'other' spans)
1932 #if DEBUG_WINDING
1933 SkDebugf("%s simple\n", __FUNCTION__);
1934 #endif
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001935 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001936 other = endSpan->fOther;
1937 nextStart = endSpan->fOtherIndex;
1938 double startT = other->fTs[nextStart].fT;
1939 nextEnd = nextStart;
1940 do {
1941 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001942 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00001943 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001944 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1945 return other;
1946 }
1947 // more than one viable candidate -- measure angles to find best
1948 SkTDArray<Angle> angles;
1949 SkASSERT(startIndex - endIndex != 0);
1950 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1951 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001952 buildAngles(end, angles, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001953 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001954 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001955 int angleCount = angles.count();
1956 int firstIndex = findStartingEdge(sorted, startIndex, end);
1957 SkASSERT(firstIndex >= 0);
1958 #if DEBUG_SORT
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001959 debugShowSort(__FUNCTION__, sorted, firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001960 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001961 if (!sortable) {
1962 unsortable = true;
1963 return NULL;
1964 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001965 SkASSERT(sorted[firstIndex]->segment() == this);
1966 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001967 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
1968 sorted[firstIndex]->sign());
caryclark@google.com235f56a2012-09-14 14:19:30 +00001969 #endif
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001970 int sumMiWinding = updateWinding(endIndex, startIndex);
1971 int sumSuWinding = updateOppWinding(endIndex, startIndex);
1972 if (operand()) {
1973 SkTSwap<int>(sumMiWinding, sumSuWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001974 }
1975 int nextIndex = firstIndex + 1;
1976 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1977 const Angle* foundAngle = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001978 bool foundDone = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001979 // iterate through the angle, and compute everyone's winding
caryclark@google.com235f56a2012-09-14 14:19:30 +00001980 Segment* nextSegment;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001981 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001982 SkASSERT(nextIndex != firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001983 if (nextIndex == angleCount) {
1984 nextIndex = 0;
1985 }
1986 const Angle* nextAngle = sorted[nextIndex];
1987 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001988 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
1989 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
1990 nextAngle->end(), op, sumMiWinding, sumSuWinding,
1991 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
1992 if (activeAngle && (!foundAngle || foundDone)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001993 foundAngle = nextAngle;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001994 foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001995 }
1996 if (nextSegment->done()) {
1997 continue;
1998 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001999 if (nextSegment->windSum(nextAngle) != SK_MinS32) {
2000 continue;
2001 }
2002 Span* last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding,
2003 oppSumWinding, activeAngle, nextAngle);
2004 if (last) {
2005 *chase.append() = last;
2006#if DEBUG_WINDING
2007 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2008 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2009#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002010 }
2011 } while (++nextIndex != lastIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002012 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002013 if (!foundAngle) {
2014 return NULL;
2015 }
2016 nextStart = foundAngle->start();
2017 nextEnd = foundAngle->end();
2018 nextSegment = foundAngle->segment();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002019
caryclark@google.com235f56a2012-09-14 14:19:30 +00002020 #if DEBUG_WINDING
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002021 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2022 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002023 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002024 return nextSegment;
2025 }
caryclark@google.com47580692012-07-23 12:14:49 +00002026
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002027 // so the span needs to contain the pairing info found here
2028 // this should include the winding computed for the edge, and
2029 // what edge it connects to, and whether it is discarded
2030 // (maybe discarded == abs(winding) > 1) ?
2031 // only need derivatives for duration of sorting, add a new struct
2032 // for pairings, remove extra spans that have zero length and
2033 // reference an unused other
2034 // for coincident, the last span on the other may be marked done
2035 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002036
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002037 // if loop is exhausted, contour may be closed.
2038 // FIXME: pass in close point so we can check for closure
2039
2040 // given a segment, and a sense of where 'inside' is, return the next
2041 // segment. If this segment has an intersection, or ends in multiple
2042 // segments, find the mate that continues the outside.
2043 // note that if there are multiples, but no coincidence, we can limit
2044 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002045
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002046 // mark found segments as done
2047
caryclark@google.com15fa1382012-05-07 20:49:36 +00002048 // start is the index of the beginning T of this edge
2049 // it is guaranteed to have an end which describes a non-zero length (?)
2050 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002051 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002052 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2053 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002054 const int startIndex = nextStart;
2055 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002056 int outerWinding = winding;
2057 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002058 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002059 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2060 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002061 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002062 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002063 outerWinding = innerWinding;
2064 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002065 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002066 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002067 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2068 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002069 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002070 int end = nextExactSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002071 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002072 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002073 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002074 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002075 // mark the smaller of startIndex, endIndex done, and all adjacent
2076 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002077 #if DEBUG_WINDING
2078 SkDebugf("%s simple\n", __FUNCTION__);
2079 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002080 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002081 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002082 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002083 double startT = other->fTs[nextStart].fT;
2084 nextEnd = nextStart;
2085 do {
2086 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002087 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002088 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00002089 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002090 return other;
2091 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002092 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002093 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002094 SkASSERT(startIndex - endIndex != 0);
2095 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002096 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002097 buildAngles(end, angles, false);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002098 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002099 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002100 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002101 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002102 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002103 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002104 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002105 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002106 if (!sortable) {
2107 unsortable = true;
2108 return NULL;
2109 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002110 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002111 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002112 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002113 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002114 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002115 int nextIndex = firstIndex + 1;
2116 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2117 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002118 // FIXME: found done logic probably fails if there are more than 4
2119 // sorted angles. It should bias towards the first and last undone
2120 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002121 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002122 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002123 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002124 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002125 bool altFlipped = false;
2126 bool foundFlipped = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002127 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002128 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002129 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002130 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002131 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002132 nextIndex = 0;
2133 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002134 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002135 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002136 if (sumWinding) {
2137 lastNonZeroSum = sumWinding;
2138 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002139 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002140 bool nextDone = nextSegment->done(nextAngle);
2141 bool nextTiny = nextSegment->tiny(nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002142 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002143 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002144 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002145 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002146 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2147 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002148 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002149 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002150 if (!active) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002151 // FIXME: shouldn't this call mark and chase done ?
caryclark@google.com59823f72012-08-09 18:17:47 +00002152 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002153 // FIXME: shouldn't this call mark and chase winding ?
caryclark@google.com47580692012-07-23 12:14:49 +00002154 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002155 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002156 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002157 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002158 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002159 return NULL;
2160 }
caryclark@google.com47580692012-07-23 12:14:49 +00002161 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002162 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002163 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002164 foundFlipped = altFlipped;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002165 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002166 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002167 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002168
caryclark@google.com24bec792012-08-20 12:43:57 +00002169 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002170 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002171 if (foundAngle && foundDone2) {
2172 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002173 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002174 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002175 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002176 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002177 foundFlipped = altFlipped;
2178 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002179 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002180 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002181 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002182 }
2183 // if the winding is non-zero, nextAngle does not connect to
2184 // current chain. If we haven't done so already, mark the angle
2185 // as done, record the winding value, and mark connected unambiguous
2186 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002187 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002188 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002189 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002190 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002191 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002192 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002193 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002194 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002195 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002196 }
2197 if (last) {
2198 *chase.append() = last;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002199 #if DEBUG_WINDING
2200 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2201 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2202 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002203 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002204 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002205 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002206 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002207 if (!foundAngle) {
2208 return NULL;
2209 }
2210 nextStart = foundAngle->start();
2211 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002212 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002213 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002214 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2215 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002216 if (winding) {
2217 #if DEBUG_WINDING
2218 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2219 if (foundSum == SK_MinS32) {
2220 SkDebugf("?");
2221 } else {
2222 SkDebugf("%d", foundSum);
2223 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002224 SkDebugf("\n");
2225 #endif
2226 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002227 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002228 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002229 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002230 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002231 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002232 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002233
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002234 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002235 const int startIndex = nextStart;
2236 const int endIndex = nextEnd;
2237 SkASSERT(startIndex != endIndex);
2238 int count = fTs.count();
2239 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2240 : startIndex > 0);
2241 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002242 int end = nextExactSpan(startIndex, step);
caryclark@google.com24bec792012-08-20 12:43:57 +00002243 SkASSERT(end >= 0);
2244 Span* endSpan = &fTs[end];
2245 Segment* other;
2246 markDone(SkMin32(startIndex, endIndex), 1);
2247 if (isSimple(end)) {
2248 #if DEBUG_WINDING
2249 SkDebugf("%s simple\n", __FUNCTION__);
2250 #endif
2251 other = endSpan->fOther;
2252 nextStart = endSpan->fOtherIndex;
2253 double startT = other->fTs[nextStart].fT;
2254 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002255 if ((approximately_less_than_zero(startT) && step < 0)
2256 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002257 step = -step;
2258 SkDEBUGCODE(firstLoop = false;)
2259 }
2260 do {
2261 nextEnd = nextStart;
2262 do {
2263 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002264 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002265 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002266 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2267 break;
2268 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002269 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002270 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002271 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002272 SkDEBUGCODE(firstLoop = false;)
2273 step = -step;
2274 } while (true);
2275 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2276 return other;
2277 }
2278 SkTDArray<Angle> angles;
2279 SkASSERT(startIndex - endIndex != 0);
2280 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2281 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002282 buildAngles(end, angles, false);
caryclark@google.com24bec792012-08-20 12:43:57 +00002283 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002284 bool sortable = SortAngles(angles, sorted);
caryclark@google.com24bec792012-08-20 12:43:57 +00002285 int angleCount = angles.count();
2286 int firstIndex = findStartingEdge(sorted, startIndex, end);
2287 SkASSERT(firstIndex >= 0);
2288 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002289 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002290 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002291 if (!sortable) {
2292 unsortable = true;
2293 return NULL;
2294 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002295 SkASSERT(sorted[firstIndex]->segment() == this);
2296 int nextIndex = firstIndex + 1;
2297 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2298 const Angle* nextAngle;
2299 Segment* nextSegment;
2300 do {
2301 if (nextIndex == angleCount) {
2302 nextIndex = 0;
2303 }
2304 nextAngle = sorted[nextIndex];
2305 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002306 if (!nextSegment->done(nextAngle)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002307 break;
2308 }
2309 if (++nextIndex == lastIndex) {
2310 return NULL;
2311 }
2312 } while (true);
2313 nextStart = nextAngle->start();
2314 nextEnd = nextAngle->end();
2315 return nextSegment;
2316 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002317
2318 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2319 int angleCount = sorted.count();
2320 int firstIndex = -1;
2321 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2322 const Angle* angle = sorted[angleIndex];
2323 if (angle->segment() == this && angle->start() == end &&
2324 angle->end() == start) {
2325 firstIndex = angleIndex;
2326 break;
2327 }
2328 }
2329 return firstIndex;
2330 }
2331
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002332 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com4eeda372012-12-06 21:47:48 +00002333 void findTooCloseToCall() {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002334 int count = fTs.count();
2335 if (count < 3) { // require t=0, x, 1 at minimum
2336 return;
2337 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002338 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002339 int moCount;
2340 Span* match;
2341 Segment* mOther;
2342 do {
2343 match = &fTs[matchIndex];
2344 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002345 // FIXME: allow quads, cubics to be near coincident?
2346 if (mOther->fVerb == SkPath::kLine_Verb) {
2347 moCount = mOther->fTs.count();
2348 if (moCount >= 3) {
2349 break;
2350 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002351 }
2352 if (++matchIndex >= count) {
2353 return;
2354 }
2355 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002356 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002357 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002358 // look for a pair of nearby T values that map to the same (x,y) value
2359 // if found, see if the pair of other segments share a common point. If
2360 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002361 for (int index = matchIndex + 1; index < count; ++index) {
2362 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002363 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002364 continue;
2365 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002366 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002367 if (tOther->fVerb != SkPath::kLine_Verb) {
2368 continue; // FIXME: allow quads, cubics to be near coincident?
2369 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002370 int toCount = tOther->fTs.count();
2371 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002372 continue;
2373 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002374 const SkPoint* testPt = &xyAtT(test);
2375 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002376 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002377 moCount = toCount;
2378 match = test;
2379 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002380 matchPt = testPt;
2381 continue;
2382 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002383 int moStart = -1;
2384 int moEnd = -1;
2385 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002386 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002387 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002388 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002389 continue;
2390 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002391 if (moSpan.fOther == this) {
2392 if (moSpan.fOtherT == match->fT) {
2393 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002394 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002395 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002396 continue;
2397 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002398 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002399 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2400 moStart = -1;
2401 break;
2402 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002403 SkASSERT(moEnd == -1);
2404 moEnd = moIndex;
2405 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002406 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002407 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002408 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002409 continue;
2410 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002411 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002412 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002413 continue;
2414 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002415 int toStart = -1;
2416 int toEnd = -1;
2417 double toStartT, toEndT;
2418 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2419 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002420 if (toSpan.fDone) {
2421 continue;
2422 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002423 if (toSpan.fOther == this) {
2424 if (toSpan.fOtherT == test->fT) {
2425 toStart = toIndex;
2426 toStartT = toSpan.fT;
2427 }
2428 continue;
2429 }
2430 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002431 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2432 moStart = -1;
2433 break;
2434 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002435 SkASSERT(toEnd == -1);
2436 toEnd = toIndex;
2437 toEndT = toSpan.fT;
2438 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002439 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002440 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2441 if (toStart <= 0 || toEnd <= 0) {
2442 continue;
2443 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002444 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002445 continue;
2446 }
2447 // test to see if the segment between there and here is linear
2448 if (!mOther->isLinear(moStart, moEnd)
2449 || !tOther->isLinear(toStart, toEnd)) {
2450 continue;
2451 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002452 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002453 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002454 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002455 } else {
caryclark@google.com4eeda372012-12-06 21:47:48 +00002456 mOther->addTCoincident(moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002457 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002458 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002459 }
2460
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002461 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002462 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002463 // a) mark spans with either end unsortable as done, or
2464 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2465 // when encountering an unsortable span
2466
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002467 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2468 // and use more concise logic like the old edge walker code?
2469 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002470 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002471 // iterate through T intersections and return topmost
2472 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002473 SkASSERT(!done());
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002474 int firstT = -1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002475 SkPoint topPt;
2476 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002477 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002478 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002479 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002480 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002481 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002482 const Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00002483 if (span.fUnsortableStart | lastUnsortable) {
2484 goto next;
2485 }
2486 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002487 const SkPoint& intercept = xyAtT(&span);
2488 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2489 && topPt.fX > intercept.fX)) {
2490 topPt = intercept;
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002491 firstT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002492 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002493 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002494 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002495 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002496 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002497 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002498 SkASSERT(firstT >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002499 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002500 int step = 1;
2501 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002502 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002503 step = -1;
2504 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002505 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002506 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002507 // if the topmost T is not on end, or is three-way or more, find left
2508 // look for left-ness from tLeft to firstT (matching y of other)
2509 SkTDArray<Angle> angles;
2510 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002511 addTwoAngles(end, firstT, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002512 buildAngles(firstT, angles, true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002513 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002514 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002515 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002516 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002517 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00002518 if (!sortable) {
2519 return NULL;
2520 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002521 // skip edges that have already been processed
2522 firstT = -1;
2523 Segment* leftSegment;
2524 do {
2525 const Angle* angle = sorted[++firstT];
caryclark@google.comf839c032012-10-26 21:03:50 +00002526 SkASSERT(!angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002527 leftSegment = angle->segment();
2528 tIndex = angle->end();
2529 endIndex = angle->start();
2530 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002531 return leftSegment;
2532 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002533
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002534 // FIXME: not crazy about this
2535 // when the intersections are performed, the other index is into an
2536 // incomplete array. as the array grows, the indices become incorrect
2537 // while the following fixes the indices up again, it isn't smart about
2538 // skipping segments whose indices are already correct
2539 // assuming we leave the code that wrote the index in the first place
2540 void fixOtherTIndex() {
2541 int iCount = fTs.count();
2542 for (int i = 0; i < iCount; ++i) {
2543 Span& iSpan = fTs[i];
2544 double oT = iSpan.fOtherT;
2545 Segment* other = iSpan.fOther;
2546 int oCount = other->fTs.count();
2547 for (int o = 0; o < oCount; ++o) {
2548 Span& oSpan = other->fTs[o];
2549 if (oT == oSpan.fT && this == oSpan.fOther) {
2550 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002551 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002552 }
2553 }
2554 }
2555 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002556
caryclark@google.com4eeda372012-12-06 21:47:48 +00002557 void init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002558 fDoneSpans = 0;
2559 fOperand = operand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00002560 fXor = evenOdd;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002561 fPts = pts;
2562 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002563 }
2564
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002565 void initWinding(int start, int end, int winding, int oppWinding) {
2566 int local = spanSign(start, end);
2567 if (local * winding >= 0) {
2568 winding += local;
2569 }
2570 local = oppSign(start, end);
2571 if (local * oppWinding >= 0) {
2572 oppWinding += local;
2573 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002574 (void) markAndChaseWinding(start, end, winding, oppWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002575 }
2576
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002577 bool intersected() const {
2578 return fTs.count() > 0;
2579 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002580
2581 bool isConnected(int startIndex, int endIndex) const {
2582 return fTs[startIndex].fWindSum != SK_MinS32
2583 || fTs[endIndex].fWindSum != SK_MinS32;
2584 }
2585
caryclark@google.com235f56a2012-09-14 14:19:30 +00002586 bool isHorizontal() const {
2587 return fBounds.fTop == fBounds.fBottom;
2588 }
2589
caryclark@google.com15fa1382012-05-07 20:49:36 +00002590 bool isLinear(int start, int end) const {
2591 if (fVerb == SkPath::kLine_Verb) {
2592 return true;
2593 }
2594 if (fVerb == SkPath::kQuad_Verb) {
2595 SkPoint qPart[3];
2596 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2597 return QuadIsLinear(qPart);
2598 } else {
2599 SkASSERT(fVerb == SkPath::kCubic_Verb);
2600 SkPoint cPart[4];
2601 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2602 return CubicIsLinear(cPart);
2603 }
2604 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002605
2606 // OPTIMIZE: successive calls could start were the last leaves off
2607 // or calls could specialize to walk forwards or backwards
2608 bool isMissing(double startT) const {
2609 size_t tCount = fTs.count();
2610 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002611 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002612 return false;
2613 }
2614 }
2615 return true;
2616 }
2617
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002618 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002619 int count = fTs.count();
2620 if (count == 2) {
2621 return true;
2622 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002623 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002624 if (approximately_less_than_zero(t)) {
2625 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002626 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002627 if (approximately_greater_than_one(t)) {
2628 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002629 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002630 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002631 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002632
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002633 bool isVertical() const {
2634 return fBounds.fLeft == fBounds.fRight;
2635 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002636
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002637 SkScalar leftMost(int start, int end) const {
2638 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2639 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002640
caryclark@google.com495f8e42012-05-31 13:13:11 +00002641 // this span is excluded by the winding rule -- chase the ends
2642 // as long as they are unambiguous to mark connections as done
2643 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002644 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002645 int index = angle->start();
2646 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002647 return markAndChaseDone(index, endIndex, winding);
2648 }
2649
caryclark@google.com31143cf2012-11-09 22:14:19 +00002650 Span* markAndChaseDone(int index, int endIndex, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002651 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002652 int min = SkMin32(index, endIndex);
2653 markDone(min, winding);
2654 Span* last;
2655 Segment* other = this;
2656 while ((other = other->nextChase(index, step, min, last))) {
2657 other->markDone(min, winding);
2658 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002659 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002660 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002661
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002662 Span* markAndChaseDoneBinary(const Angle* angle, int winding, int oppWinding) {
2663 int index = angle->start();
2664 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002665 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002666 int min = SkMin32(index, endIndex);
2667 markDoneBinary(min, winding, oppWinding);
2668 Span* last;
2669 Segment* other = this;
2670 while ((other = other->nextChase(index, step, min, last))) {
2671 other->markDoneBinary(min, winding, oppWinding);
2672 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002673 return last;
2674 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002675
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002676 Span* markAndChaseDoneBinary(int index, int endIndex) {
2677 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002678 int min = SkMin32(index, endIndex);
2679 markDoneBinary(min);
2680 Span* last;
2681 Segment* other = this;
2682 while ((other = other->nextChase(index, step, min, last))) {
2683 other->markDoneBinary(min);
2684 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002685 return last;
2686 }
2687
caryclark@google.com4eeda372012-12-06 21:47:48 +00002688 Span* markAndChaseWinding(const Angle* angle, const int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002689 int index = angle->start();
2690 int endIndex = angle->end();
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002691 int step = SkSign32(endIndex - index);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002692 int min = SkMin32(index, endIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002693 markWinding(min, winding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002694 Span* last;
2695 Segment* other = this;
2696 while ((other = other->nextChase(index, step, min, last))) {
2697 if (other->fTs[min].fWindSum != SK_MinS32) {
2698 SkASSERT(other->fTs[min].fWindSum == winding);
2699 return NULL;
2700 }
2701 other->markWinding(min, winding);
2702 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002703 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002704 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002705
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002706 Span* markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002707 int min = SkMin32(index, endIndex);
2708 int step = SkSign32(endIndex - index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002709 markWinding(min, winding, oppWinding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002710 Span* last;
2711 Segment* other = this;
2712 while ((other = other->nextChase(index, step, min, last))) {
2713 if (other->fTs[min].fWindSum != SK_MinS32) {
2714 SkASSERT(other->fTs[min].fWindSum == winding);
2715 return NULL;
2716 }
2717 other->markWinding(min, winding, oppWinding);
2718 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002719 return last;
2720 }
2721
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002722 Span* markAndChaseWinding(const Angle* angle, int winding, int oppWinding) {
2723 int start = angle->start();
2724 int end = angle->end();
2725 return markAndChaseWinding(start, end, winding, oppWinding);
2726 }
2727
2728 Span* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
2729 bool activeAngle, const Angle* angle) {
2730 SkASSERT(angle->segment() == this);
2731 if (useInnerWinding(maxWinding, sumWinding)) {
2732 maxWinding = sumWinding;
2733 }
2734 if (oppMaxWinding != oppSumWinding && useInnerWinding(oppMaxWinding, oppSumWinding)) {
2735 oppMaxWinding = oppSumWinding;
2736 }
2737 Span* last;
2738 if (activeAngle) {
2739 last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
2740 } else {
2741 last = markAndChaseDoneBinary(angle, maxWinding, oppMaxWinding);
2742 }
2743 return last;
2744 }
2745
caryclark@google.com495f8e42012-05-31 13:13:11 +00002746 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002747 // This may be called when the segment is already marked done. While this
2748 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002749 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002750 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002751 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002752 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002753 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002754 double referenceT = fTs[index].fT;
2755 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002756 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2757 markOneDone(__FUNCTION__, lesser, winding);
2758 }
2759 do {
2760 markOneDone(__FUNCTION__, index, winding);
2761 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002762 }
2763
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002764 void markDoneBinary(int index, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002765 // SkASSERT(!done());
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002766 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002767 double referenceT = fTs[index].fT;
2768 int lesser = index;
2769 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002770 markOneDoneBinary(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002771 }
2772 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002773 markOneDoneBinary(__FUNCTION__, index, winding, oppWinding);
2774 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2775 }
2776
2777 void markDoneBinary(int index) {
2778 double referenceT = fTs[index].fT;
2779 int lesser = index;
2780 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2781 markOneDoneBinary(__FUNCTION__, lesser);
2782 }
2783 do {
2784 markOneDoneBinary(__FUNCTION__, index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002785 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002786 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002787
caryclark@google.com24bec792012-08-20 12:43:57 +00002788 void markOneDone(const char* funName, int tIndex, int winding) {
2789 Span* span = markOneWinding(funName, tIndex, winding);
2790 if (!span) {
2791 return;
2792 }
2793 span->fDone = true;
2794 fDoneSpans++;
2795 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002796
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002797 void markOneDoneBinary(const char* funName, int tIndex) {
2798 Span* span = verifyOneWinding(funName, tIndex);
2799 if (!span) {
2800 return;
2801 }
2802 span->fDone = true;
2803 fDoneSpans++;
2804 }
2805
2806 void markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002807 Span* span = markOneWinding(funName, tIndex, winding, oppWinding);
2808 if (!span) {
2809 return;
2810 }
2811 span->fDone = true;
2812 fDoneSpans++;
2813 }
2814
caryclark@google.com24bec792012-08-20 12:43:57 +00002815 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2816 Span& span = fTs[tIndex];
2817 if (span.fDone) {
2818 return NULL;
2819 }
2820 #if DEBUG_MARK_DONE
2821 debugShowNewWinding(funName, span, winding);
2822 #endif
2823 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002824 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002825 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002826 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002827 span.fWindSum = winding;
2828 return &span;
2829 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002830
caryclark@google.com31143cf2012-11-09 22:14:19 +00002831 Span* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding) {
2832 Span& span = fTs[tIndex];
2833 if (span.fDone) {
2834 return NULL;
2835 }
2836 #if DEBUG_MARK_DONE
2837 debugShowNewWinding(funName, span, winding, oppWinding);
2838 #endif
2839 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
2840 #ifdef SK_DEBUG
2841 SkASSERT(abs(winding) <= gDebugMaxWindSum);
2842 #endif
2843 span.fWindSum = winding;
2844 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
2845 #ifdef SK_DEBUG
2846 SkASSERT(abs(oppWinding) <= gDebugMaxWindSum);
2847 #endif
2848 span.fOppSum = oppWinding;
2849 return &span;
2850 }
2851
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002852 Span* verifyOneWinding(const char* funName, int tIndex) {
2853 Span& span = fTs[tIndex];
2854 if (span.fDone) {
2855 return NULL;
2856 }
2857 #if DEBUG_MARK_DONE
2858 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
2859 #endif
2860 SkASSERT(span.fWindSum != SK_MinS32);
2861 SkASSERT(span.fOppSum != SK_MinS32);
2862 return &span;
2863 }
2864
caryclark@google.comf839c032012-10-26 21:03:50 +00002865 // note that just because a span has one end that is unsortable, that's
2866 // not enough to mark it done. The other end may be sortable, allowing the
2867 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002868 void markUnsortable(int start, int end) {
2869 Span* span = &fTs[start];
2870 if (start < end) {
2871 span->fUnsortableStart = true;
2872 } else {
2873 --span;
2874 span->fUnsortableEnd = true;
2875 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002876 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002877 return;
2878 }
2879 span->fDone = true;
2880 fDoneSpans++;
2881 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002882
caryclark@google.com59823f72012-08-09 18:17:47 +00002883 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002884 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002885 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002886 double referenceT = fTs[index].fT;
2887 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002888 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2889 markOneWinding(__FUNCTION__, lesser, winding);
2890 }
2891 do {
2892 markOneWinding(__FUNCTION__, index, winding);
2893 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002894 }
2895
2896 void markWinding(int index, int winding, int oppWinding) {
2897 // SkASSERT(!done());
caryclark@google.com4eeda372012-12-06 21:47:48 +00002898 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002899 double referenceT = fTs[index].fT;
2900 int lesser = index;
2901 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2902 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002903 }
2904 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002905 markOneWinding(__FUNCTION__, index, winding, oppWinding);
2906 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002907 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002908
caryclark@google.com2ddff932012-08-07 21:25:27 +00002909 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002910 int nextDoorWind = SK_MaxS32;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002911 int nextOppWind = SK_MaxS32;
caryclark@google.com0c803d02012-08-06 11:15:47 +00002912 if (tIndex > 0) {
2913 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002914 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002915 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002916 nextOppWind = below.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00002917 }
2918 }
2919 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2920 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002921 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002922 nextDoorWind = above.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002923 nextOppWind = above.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00002924 }
2925 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002926 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2927 const Span& below = fTs[tIndex - 1];
2928 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002929 nextOppWind = below.fOppValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002930 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002931 if (nextDoorWind != SK_MaxS32) {
2932 Span& newSpan = fTs[tIndex];
2933 newSpan.fWindValue = nextDoorWind;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002934 newSpan.fOppValue = nextOppWind;
caryclark@google.com4eeda372012-12-06 21:47:48 +00002935 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002936 newSpan.fDone = true;
2937 ++fDoneSpans;
2938 }
2939 }
2940 }
2941
caryclark@google.com9764cc62012-07-12 19:29:45 +00002942 // return span if when chasing, two or more radiating spans are not done
2943 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2944 // candidate and the remaining spans have windValue == 0 (canceled by
2945 // coincidence). The coincident edges could either be removed altogether,
2946 // or this code could be more complicated in detecting this case. Worth it?
2947 bool multipleSpans(int end) const {
2948 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002949 }
2950
caryclark@google.com4eeda372012-12-06 21:47:48 +00002951 Segment* nextChase(int& index, const int step, int& min, Span*& last) const {
2952 int end = nextExactSpan(index, step);
2953 SkASSERT(end >= 0);
2954 if (multipleSpans(end)) {
2955 last = &fTs[end];
2956 return NULL;
2957 }
2958 const Span& endSpan = fTs[end];
2959 Segment* other = endSpan.fOther;
2960 index = endSpan.fOtherIndex;
2961 int otherEnd = other->nextExactSpan(index, step);
2962 min = SkMin32(index, otherEnd);
2963 return other;
2964 }
2965
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002966 // This has callers for two different situations: one establishes the end
2967 // of the current span, and one establishes the beginning of the next span
2968 // (thus the name). When this is looking for the end of the current span,
2969 // coincidence is found when the beginning Ts contain -step and the end
2970 // contains step. When it is looking for the beginning of the next, the
2971 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002972 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002973 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002974 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002975 int count = fTs.count();
2976 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002977 while (step > 0 ? ++to < count : --to >= 0) {
2978 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002979 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002980 continue;
2981 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002982 return to;
2983 }
2984 return -1;
2985 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00002986
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002987 // FIXME
2988 // this returns at any difference in T, vs. a preset minimum. It may be
2989 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002990 // OPTIMIZATION splitting this into separate loops for up/down steps
2991 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002992 int nextExactSpan(int from, int step) const {
2993 const Span& fromSpan = fTs[from];
2994 int count = fTs.count();
2995 int to = from;
2996 while (step > 0 ? ++to < count : --to >= 0) {
2997 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00002998 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002999 continue;
3000 }
3001 return to;
3002 }
3003 return -1;
3004 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003005
caryclark@google.com235f56a2012-09-14 14:19:30 +00003006 bool operand() const {
3007 return fOperand;
3008 }
3009
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003010 int oppSign(const Angle* angle) const {
3011 SkASSERT(angle->segment() == this);
3012 return oppSign(angle->start(), angle->end());
3013 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003014
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003015 int oppSign(int startIndex, int endIndex) const {
3016 int result = startIndex < endIndex ? -fTs[startIndex].fOppValue
3017 : fTs[endIndex].fOppValue;
3018#if DEBUG_WIND_BUMP
3019 SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
3020#endif
3021 return result;
3022 }
3023
caryclark@google.com31143cf2012-11-09 22:14:19 +00003024 int oppSum(int tIndex) const {
3025 return fTs[tIndex].fOppSum;
3026 }
3027
3028 int oppSum(const Angle* angle) const {
3029 int lesser = SkMin32(angle->start(), angle->end());
3030 return fTs[lesser].fOppSum;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003031 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003032
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003033 int oppValue(int tIndex) const {
3034 return fTs[tIndex].fOppValue;
3035 }
3036
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003037 const SkPoint* pts() const {
3038 return fPts;
3039 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003040
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003041 void reset() {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003042 init(NULL, (SkPath::Verb) -1, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003043 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
3044 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003045 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00003046
caryclark@google.com4eeda372012-12-06 21:47:48 +00003047 void setOppXor(bool isOppXor) {
3048 fOppXor = isOppXor;
3049 }
3050
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003051 void setUpWindings(int index, int endIndex, int& sumMiWinding, int& sumSuWinding,
3052 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
3053 int deltaSum = spanSign(index, endIndex);
3054 int oppDeltaSum = oppSign(index, endIndex);
3055 if (operand()) {
3056 maxWinding = sumSuWinding;
3057 sumWinding = sumSuWinding -= deltaSum;
3058 oppMaxWinding = sumMiWinding;
3059 oppSumWinding = sumMiWinding -= oppDeltaSum;
3060 } else {
3061 maxWinding = sumMiWinding;
3062 sumWinding = sumMiWinding -= deltaSum;
3063 oppMaxWinding = sumSuWinding;
3064 oppSumWinding = sumSuWinding -= oppDeltaSum;
3065 }
3066 }
3067
caryclark@google.comf839c032012-10-26 21:03:50 +00003068 // This marks all spans unsortable so that this info is available for early
3069 // exclusion in find top and others. This could be optimized to only mark
3070 // adjacent spans that unsortable. However, this makes it difficult to later
3071 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003072 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003073 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003074 int angleCount = angles.count();
3075 int angleIndex;
3076 angleList.setReserve(angleCount);
3077 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003078 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00003079 *angleList.append() = &angle;
3080 sortable &= !angle.unsortable();
3081 }
3082 if (sortable) {
3083 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3084 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3085 if (angles[angleIndex].unsortable()) {
3086 sortable = false;
3087 break;
3088 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003089 }
3090 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003091 if (!sortable) {
3092 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3093 Angle& angle = angles[angleIndex];
3094 angle.segment()->markUnsortable(angle.start(), angle.end());
3095 }
3096 }
3097 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003098 }
3099
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003100 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003101 const Span& span(int tIndex) const {
3102 return fTs[tIndex];
3103 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003104
caryclark@google.com235f56a2012-09-14 14:19:30 +00003105 int spanSign(const Angle* angle) const {
3106 SkASSERT(angle->segment() == this);
3107 return spanSign(angle->start(), angle->end());
3108 }
3109
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003110 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003111 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue
3112 : fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003113#if DEBUG_WIND_BUMP
3114 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3115#endif
3116 return result;
3117 }
3118
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003119 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003120 double t(int tIndex) const {
3121 return fTs[tIndex].fT;
3122 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003123
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003124 bool tiny(const Angle* angle) const {
3125 int start = angle->start();
3126 int end = angle->end();
caryclark@google.comf839c032012-10-26 21:03:50 +00003127 const Span& mSpan = fTs[SkMin32(start, end)];
3128 return mSpan.fTiny;
3129 }
3130
caryclark@google.com18063442012-07-25 12:05:18 +00003131 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3132 double start) {
3133 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003134 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003135 *outsideTs.append() = end;
3136 *outsideTs.append() = start;
3137 }
3138 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00003139
caryclark@google.com24bec792012-08-20 12:43:57 +00003140 void undoneSpan(int& start, int& end) {
3141 size_t tCount = fTs.count();
3142 size_t index;
3143 for (index = 0; index < tCount; ++index) {
3144 if (!fTs[index].fDone) {
3145 break;
3146 }
3147 }
3148 SkASSERT(index < tCount - 1);
3149 start = index;
3150 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003151 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003152 SkASSERT(index < tCount);
3153 SkASSERT(index < tCount);
3154 end = index;
3155 }
caryclark@google.com18063442012-07-25 12:05:18 +00003156
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003157 bool unsortable(int index) const {
3158 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3159 }
3160
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003161 void updatePts(const SkPoint pts[]) {
3162 fPts = pts;
3163 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003164
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003165 int updateOppWinding(int index, int endIndex) const {
3166 int lesser = SkMin32(index, endIndex);
3167 int oppWinding = oppSum(lesser);
3168 int oppSpanWinding = oppSign(index, endIndex);
3169 if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)) {
3170 oppWinding -= oppSpanWinding;
3171 }
3172 return oppWinding;
3173 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00003174
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003175 int updateOppWinding(const Angle* angle) const {
3176 int startIndex = angle->start();
3177 int endIndex = angle->end();
3178 return updateOppWinding(endIndex, startIndex);
3179 }
3180
3181 int updateOppWindingReverse(const Angle* angle) const {
3182 int startIndex = angle->start();
3183 int endIndex = angle->end();
3184 return updateOppWinding(startIndex, endIndex);
3185 }
3186
3187 int updateWinding(int index, int endIndex) const {
3188 int lesser = SkMin32(index, endIndex);
3189 int winding = windSum(lesser);
3190 int spanWinding = spanSign(index, endIndex);
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00003191 if (winding && useInnerWinding(winding - spanWinding, winding)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003192 winding -= spanWinding;
3193 }
3194 return winding;
3195 }
3196
3197 int updateWinding(const Angle* angle) const {
3198 int startIndex = angle->start();
3199 int endIndex = angle->end();
3200 return updateWinding(endIndex, startIndex);
3201 }
3202
3203 int updateWindingReverse(const Angle* angle) const {
3204 int startIndex = angle->start();
3205 int endIndex = angle->end();
3206 return updateWinding(startIndex, endIndex);
3207 }
3208
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003209 SkPath::Verb verb() const {
3210 return fVerb;
3211 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003212
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003213 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003214 return fTs[tIndex].fWindSum;
3215 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003216
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003217 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003218 int start = angle->start();
3219 int end = angle->end();
3220 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003221 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003222 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003223
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003224 int windValue(int tIndex) const {
3225 return fTs[tIndex].fWindValue;
3226 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003227
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003228 int windValue(const Angle* angle) const {
3229 int start = angle->start();
3230 int end = angle->end();
3231 int index = SkMin32(start, end);
3232 return windValue(index);
3233 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003234
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003235 SkScalar xAtT(const Span* span) const {
3236 return xyAtT(span).fX;
3237 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003238
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003239 const SkPoint& xyAtT(int index) const {
3240 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003241 }
3242
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003243 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003244 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003245 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003246 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003247 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003248 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003249 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003250 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003251 }
3252 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003253 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003254 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003255
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003256 SkScalar yAtT(int index) const {
3257 return yAtT(&fTs[index]);
3258 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003259
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003260 SkScalar yAtT(const Span* span) const {
3261 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003262 }
3263
caryclark@google.com4eeda372012-12-06 21:47:48 +00003264 void zeroCoincidentOpp(Span* oTest, int index) {
3265 Span* const test = &fTs[index];
3266 Span* end = test;
3267 do {
3268 end->fOppValue = 0;
3269 end = &fTs[++index];
3270 } while (approximately_negative(end->fT - test->fT));
3271 }
3272
3273 void zeroCoincidentOther(Span* test, const double tRatio, const double oEndT, int oIndex) {
3274 Span* const oTest = &fTs[oIndex];
3275 Span* oEnd = oTest;
3276 const double startT = test->fT;
3277 const double oStartT = oTest->fT;
3278 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
3279 while (!approximately_negative(oEndT - oEnd->fT)
3280 && approximately_negative(oEnd->fT - otherTMatch)) {
3281 oEnd->fOppValue = 0;
3282 oEnd = &fTs[++oIndex];
3283 }
3284 }
3285
3286 void zeroSpan(Span* span) {
3287 SkASSERT(span->fWindValue > 0 || span->fOppValue > 0);
caryclark@google.com6ec15262012-11-16 20:16:50 +00003288 span->fWindValue = 0;
caryclark@google.com729e1c42012-11-21 21:36:34 +00003289 span->fOppValue = 0;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003290 SkASSERT(!span->fDone);
3291 span->fDone = true;
3292 ++fDoneSpans;
caryclark@google.com6ec15262012-11-16 20:16:50 +00003293 }
3294
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003295#if DEBUG_DUMP
3296 void dump() const {
3297 const char className[] = "Segment";
3298 const int tab = 4;
3299 for (int i = 0; i < fTs.count(); ++i) {
3300 SkPoint out;
3301 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3302 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003303 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003304 tab + sizeof(className), className, fID,
3305 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003306 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003307 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003308 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003309 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003310 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003311 }
3312#endif
3313
caryclark@google.com47580692012-07-23 12:14:49 +00003314#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003315 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003316 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003317 for (int i = 0; i < fTs.count(); ++i) {
3318 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3319 return;
3320 }
3321 }
3322 SkASSERT(0);
3323 }
3324#endif
3325
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003326#if DEBUG_DUMP
3327 int debugID() const {
3328 return fID;
3329 }
3330#endif
3331
caryclark@google.com24bec792012-08-20 12:43:57 +00003332#if DEBUG_WINDING
3333 void debugShowSums() const {
3334 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3335 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3336 for (int i = 0; i < fTs.count(); ++i) {
3337 const Span& span = fTs[i];
3338 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3339 if (span.fWindSum == SK_MinS32) {
3340 SkDebugf("?");
3341 } else {
3342 SkDebugf("%d", span.fWindSum);
3343 }
3344 SkDebugf("]");
3345 }
3346 SkDebugf("\n");
3347 }
3348#endif
3349
caryclark@google.comcc905052012-07-25 20:59:42 +00003350#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003351 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003352 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003353 int lastWind = -1;
3354 int lastOpp = -1;
3355 double lastT = -1;
3356 int i;
3357 for (i = 0; i < fTs.count(); ++i) {
3358 bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
3359 || lastOpp != fTs[i].fOppValue;
3360 if (change && lastWind >= 0) {
3361 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3362 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3363 }
3364 if (change) {
3365 SkDebugf(" [o=%d", fTs[i].fOther->fID);
3366 lastWind = fTs[i].fWindValue;
3367 lastOpp = fTs[i].fOppValue;
3368 lastT = fTs[i].fT;
3369 } else {
3370 SkDebugf(",%d", fTs[i].fOther->fID);
3371 }
3372 }
3373 if (i <= 0) {
3374 return;
3375 }
3376 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3377 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3378 if (fOperand) {
3379 SkDebugf(" operand");
3380 }
3381 if (done()) {
3382 SkDebugf(" done");
caryclark@google.com47580692012-07-23 12:14:49 +00003383 }
3384 SkDebugf("\n");
3385 }
3386#endif
3387
caryclark@google.com027de222012-07-12 12:52:50 +00003388#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003389 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003390 if (done()) {
3391 return;
3392 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003393#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3394 int lastId = -1;
3395 double lastT = -1;
3396#endif
caryclark@google.com027de222012-07-12 12:52:50 +00003397 for (int i = 0; i < fTs.count(); ++i) {
3398 if (fTs[i].fDone) {
3399 continue;
3400 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003401#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3402 if (lastId == fID && lastT == fTs[i].fT) {
3403 continue;
3404 }
3405 lastId = fID;
3406 lastT = fTs[i].fT;
3407#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003408 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003409 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3410 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3411 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3412 }
3413 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003414 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003415 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003416 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003417 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3418 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3419 if (fTs[i].fWindSum == SK_MinS32) {
3420 SkDebugf("?");
3421 } else {
3422 SkDebugf("%d", fTs[i].fWindSum);
3423 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003424 SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003425 }
3426 }
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +00003427
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003428 // This isn't useful yet -- but leaving it in for now in case i think of something
3429 // to use it for
3430 void validateActiveSpans() const {
3431 if (done()) {
3432 return;
3433 }
3434 int tCount = fTs.count();
3435 for (int index = 0; index < tCount; ++index) {
3436 if (fTs[index].fDone) {
3437 continue;
3438 }
3439 // count number of connections which are not done
3440 int first = index;
3441 double baseT = fTs[index].fT;
3442 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3443 --first;
3444 }
3445 int last = index;
3446 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3447 ++last;
3448 }
3449 int connections = 0;
3450 connections += first > 0 && !fTs[first - 1].fDone;
3451 for (int test = first; test <= last; ++test) {
3452 connections += !fTs[test].fDone;
3453 const Segment* other = fTs[test].fOther;
3454 int oIndex = fTs[test].fOtherIndex;
3455 connections += !other->fTs[oIndex].fDone;
3456 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3457 }
3458 // SkASSERT(!(connections & 1));
3459 }
3460 }
caryclark@google.com027de222012-07-12 12:52:50 +00003461#endif
3462
caryclark@google.com0c803d02012-08-06 11:15:47 +00003463#if DEBUG_MARK_DONE
3464 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3465 const SkPoint& pt = xyAtT(&span);
3466 SkDebugf("%s id=%d", fun, fID);
3467 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3468 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3469 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3470 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003471 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3472 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3473 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3474 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003475 if (span.fWindSum == SK_MinS32) {
3476 SkDebugf("?");
3477 } else {
3478 SkDebugf("%d", span.fWindSum);
3479 }
3480 SkDebugf(" windValue=%d\n", span.fWindValue);
3481 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003482
3483 void debugShowNewWinding(const char* fun, const Span& span, int winding, int oppWinding) {
3484 const SkPoint& pt = xyAtT(&span);
3485 SkDebugf("%s id=%d", fun, fID);
3486 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3487 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3488 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3489 }
3490 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3491 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3492 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d newOppSum=%d oppSum=",
3493 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
3494 winding, oppWinding);
3495 if (span.fOppSum == SK_MinS32) {
3496 SkDebugf("?");
3497 } else {
3498 SkDebugf("%d", span.fOppSum);
3499 }
3500 SkDebugf(" windSum=");
3501 if (span.fWindSum == SK_MinS32) {
3502 SkDebugf("?");
3503 } else {
3504 SkDebugf("%d", span.fWindSum);
3505 }
3506 SkDebugf(" windValue=%d\n", span.fWindValue);
3507 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003508#endif
3509
caryclark@google.com47580692012-07-23 12:14:49 +00003510#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003511 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com31143cf2012-11-09 22:14:19 +00003512 const int contourWinding, const int oppContourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003513 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003514 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003515 int lastSum = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003516 int oppLastSum = oppContourWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003517 const Angle* firstAngle = angles[first];
3518 int windSum = lastSum - spanSign(firstAngle);
3519 int oppoSign = oppSign(firstAngle);
3520 int oppWindSum = oppLastSum - oppoSign;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003521 SkDebugf("%s %s contourWinding=%d oppContourWinding=%d sign=%d\n", fun, __FUNCTION__,
3522 contourWinding, oppContourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003523 int index = first;
3524 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003525 do {
3526 const Angle& angle = *angles[index];
3527 const Segment& segment = *angle.segment();
3528 int start = angle.start();
3529 int end = angle.end();
3530 const Span& sSpan = segment.fTs[start];
3531 const Span& eSpan = segment.fTs[end];
3532 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com31143cf2012-11-09 22:14:19 +00003533 bool opp = segment.fOperand ^ fOperand;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003534 if (!firstTime) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003535 oppoSign = segment.oppSign(&angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003536 if (opp) {
3537 oppLastSum = oppWindSum;
3538 oppWindSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003539 if (oppoSign) {
3540 lastSum = windSum;
3541 windSum -= oppoSign;
3542 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003543 } else {
3544 lastSum = windSum;
3545 windSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003546 if (oppoSign) {
3547 oppLastSum = oppWindSum;
3548 oppWindSum -= oppoSign;
3549 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003550 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00003551 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003552 SkDebugf("%s [%d] %sid=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00003553 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003554 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003555 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003556 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3557 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3558 mSpan.fWindValue);
3559 if (mSpan.fWindSum == SK_MinS32) {
3560 SkDebugf("?");
3561 } else {
3562 SkDebugf("%d", mSpan.fWindSum);
3563 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003564 int last, wind;
3565 if (opp) {
3566 last = oppLastSum;
3567 wind = oppWindSum;
3568 } else {
3569 last = lastSum;
3570 wind = windSum;
3571 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003572 if (!oppoSign) {
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003573 SkDebugf(" %d->%d (max=%d)", last, wind,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003574 useInnerWinding(last, wind) ? wind : last);
3575 } else {
3576 SkDebugf(" %d->%d (%d->%d)", last, wind, opp ? lastSum : oppLastSum,
3577 opp ? windSum : oppWindSum);
3578 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003579 SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003580#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003581 angle.debugShow(segment.xyAtT(&sSpan));
3582#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003583 ++index;
3584 if (index == angles.count()) {
3585 index = 0;
3586 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003587 if (firstTime) {
3588 firstTime = false;
3589 }
caryclark@google.com47580692012-07-23 12:14:49 +00003590 } while (index != first);
3591 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003592
3593 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first) {
3594 const Angle* firstAngle = angles[first];
3595 const Segment* segment = firstAngle->segment();
3596 int winding = segment->updateWinding(firstAngle);
3597 int oppWinding = segment->updateOppWinding(firstAngle);
3598 debugShowSort(fun, angles, first, winding, oppWinding);
3599 }
3600
caryclark@google.com47580692012-07-23 12:14:49 +00003601#endif
3602
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003603#if DEBUG_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003604 static char as_digit(int value) {
3605 return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
3606 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003607#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003608
caryclark@google.com729e1c42012-11-21 21:36:34 +00003609#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003610 int debugShowWindingValues(int slotCount, int ofInterest) const {
3611 if (!(1 << fID & ofInterest)) {
3612 return 0;
3613 }
3614 int sum = 0;
3615 SkTDArray<char> slots;
3616 slots.setCount(slotCount * 2);
3617 memset(slots.begin(), ' ', slotCount * 2);
3618 for (int i = 0; i < fTs.count(); ++i) {
3619 // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
3620 // continue;
3621 // }
3622 sum += fTs[i].fWindValue;
3623 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
3624 sum += fTs[i].fOppValue;
3625 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
3626 }
3627 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
3628 slots.begin() + slotCount);
3629 return sum;
3630 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003631#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003632
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003633private:
3634 const SkPoint* fPts;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003635 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003636 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com4eeda372012-12-06 21:47:48 +00003637 // OPTIMIZATION: could pack donespans, verb, operand, xor into 1 int-sized value
caryclark@google.com24bec792012-08-20 12:43:57 +00003638 int fDoneSpans; // quick check that segment is finished
caryclark@google.com4eeda372012-12-06 21:47:48 +00003639 // OPTIMIZATION: force the following to be byte-sized
3640 SkPath::Verb fVerb;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003641 bool fOperand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003642 bool fXor; // set if original contour had even-odd fill
3643 bool fOppXor; // set if opposite operand had even-odd fill
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003644#if DEBUG_DUMP
3645 int fID;
3646#endif
3647};
3648
caryclark@google.comb9738012012-07-03 19:53:30 +00003649class Contour;
3650
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003651struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003652 Contour* fContours[2];
3653 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003654 double fTs[2][2];
3655};
3656
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003657class Contour {
3658public:
3659 Contour() {
3660 reset();
3661#if DEBUG_DUMP
3662 fID = ++gContourID;
3663#endif
3664 }
3665
3666 bool operator<(const Contour& rh) const {
3667 return fBounds.fTop == rh.fBounds.fTop
3668 ? fBounds.fLeft < rh.fBounds.fLeft
3669 : fBounds.fTop < rh.fBounds.fTop;
3670 }
3671
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003672 void addCoincident(int index, Contour* other, int otherIndex,
3673 const Intersections& ts, bool swap) {
3674 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003675 coincidence.fContours[0] = this; // FIXME: no need to store
caryclark@google.comb9738012012-07-03 19:53:30 +00003676 coincidence.fContours[1] = other;
3677 coincidence.fSegments[0] = index;
3678 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003679 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3680 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3681 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3682 coincidence.fTs[swap][0] = ts.fT[0][0];
3683 coincidence.fTs[swap][1] = ts.fT[0][1];
3684 coincidence.fTs[!swap][0] = ts.fT[1][0];
3685 coincidence.fTs[!swap][1] = ts.fT[1][1];
3686 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3687 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3688 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3689 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3690 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3691 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3692 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003693 }
3694
3695 void addCross(const Contour* crosser) {
3696#ifdef DEBUG_CROSS
3697 for (int index = 0; index < fCrosses.count(); ++index) {
3698 SkASSERT(fCrosses[index] != crosser);
3699 }
3700#endif
3701 *fCrosses.append() = crosser;
3702 }
3703
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003704 void addCubic(const SkPoint pts[4]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003705 fSegments.push_back().addCubic(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003706 fContainsCurves = true;
3707 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003708
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003709 int addLine(const SkPoint pts[2]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003710 fSegments.push_back().addLine(pts, fOperand, fXor);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003711 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003712 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003713
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003714 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3715 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3716 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003717
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003718 int addQuad(const SkPoint pts[3]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003719 fSegments.push_back().addQuad(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003720 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003721 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003722 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003723
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003724 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3725 containsIntercepts();
3726 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3727 }
3728
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003729 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003730 return fBounds;
3731 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003732
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003733 void complete() {
3734 setBounds();
3735 fContainsIntercepts = false;
3736 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003737
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003738 void containsIntercepts() {
3739 fContainsIntercepts = true;
3740 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003741
rmistry@google.comd6176b02012-08-23 18:14:13 +00003742 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003743 int &tIndex, double& hitT) {
3744 int segmentCount = fSegments.count();
3745 const Segment* bestSegment = NULL;
3746 for (int test = 0; test < segmentCount; ++test) {
3747 Segment* testSegment = &fSegments[test];
3748 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003749 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003750 continue;
3751 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003752 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003753 continue;
3754 }
3755 if (bounds.fLeft > basePt.fX) {
3756 continue;
3757 }
3758 if (bounds.fRight < basePt.fX) {
3759 continue;
3760 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003761 if (bounds.fLeft == bounds.fRight) {
3762 continue;
3763 }
3764 #if 0
3765 bool leftHalf = bounds.fLeft == basePt.fX;
3766 bool rightHalf = bounds.fRight == basePt.fX;
3767 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3768 basePt, leftHalf, rightHalf)) {
3769 continue;
3770 }
3771 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003772 double testHitT;
3773 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3774 if (testT >= 0) {
3775 bestSegment = testSegment;
3776 tIndex = testT;
3777 hitT = testHitT;
3778 }
3779 }
3780 return bestSegment;
3781 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003782
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003783 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003784 for (int index = 0; index < fCrosses.count(); ++index) {
3785 if (fCrosses[index] == crosser) {
3786 return true;
3787 }
3788 }
3789 return false;
3790 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003791
caryclark@google.comf839c032012-10-26 21:03:50 +00003792 const SkPoint& end() const {
3793 const Segment& segment = fSegments.back();
3794 return segment.pts()[segment.verb()];
3795 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003796
caryclark@google.com4eeda372012-12-06 21:47:48 +00003797 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003798 int segmentCount = fSegments.count();
3799 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003800 fSegments[sIndex].findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003801 }
3802 }
3803
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003804 void fixOtherTIndex() {
3805 int segmentCount = fSegments.count();
3806 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3807 fSegments[sIndex].fixOtherTIndex();
3808 }
3809 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003810
caryclark@google.com31143cf2012-11-09 22:14:19 +00003811 bool operand() const {
3812 return fOperand;
3813 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003814
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003815 void reset() {
3816 fSegments.reset();
3817 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003818 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003819 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003820
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003821 void resolveCoincidence(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003822 int count = fCoincidences.count();
3823 for (int index = 0; index < count; ++index) {
3824 Coincidence& coincidence = fCoincidences[index];
caryclark@google.com4eeda372012-12-06 21:47:48 +00003825 SkASSERT(coincidence.fContours[0] == this);
caryclark@google.comb9738012012-07-03 19:53:30 +00003826 int thisIndex = coincidence.fSegments[0];
caryclark@google.com4eeda372012-12-06 21:47:48 +00003827 Segment& thisOne = fSegments[thisIndex];
3828 if (thisOne.done()) {
3829 continue;
3830 }
3831 Contour* otherContour = coincidence.fContours[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00003832 int otherIndex = coincidence.fSegments[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00003833 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00003834 if (other.done()) {
3835 continue;
3836 }
caryclark@google.com47580692012-07-23 12:14:49 +00003837 #if DEBUG_CONCIDENT
3838 thisOne.debugShowTs();
3839 other.debugShowTs();
3840 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003841 double startT = coincidence.fTs[0][0];
3842 double endT = coincidence.fTs[0][1];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003843 bool cancelers = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003844 if (startT > endT) {
3845 SkTSwap<double>(startT, endT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003846 cancelers ^= true; // FIXME: just assign true
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003847 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003848 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003849 double oStartT = coincidence.fTs[1][0];
3850 double oEndT = coincidence.fTs[1][1];
3851 if (oStartT > oEndT) {
3852 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003853 cancelers ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003854 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003855 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00003856 bool opp = fOperand ^ otherContour->fOperand;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003857 if (cancelers && !opp) {
3858 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00003859 if (startT > 0 || oEndT < 1
3860 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3861 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003862 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003863 if (oStartT > 0 || endT < 1
3864 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3865 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003866 }
3867 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003868 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003869 if (startT > 0 || oStartT > 0
3870 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003871 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003872 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003873 if (endT < 1 || oEndT < 1
3874 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003875 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003876 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003877 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003878 }
caryclark@google.com47580692012-07-23 12:14:49 +00003879 #if DEBUG_CONCIDENT
3880 thisOne.debugShowTs();
3881 other.debugShowTs();
3882 #endif
caryclark@google.com729e1c42012-11-21 21:36:34 +00003883 #if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003884 debugShowWindingValues(contourList);
3885 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003886 }
3887 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003888
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003889 const SkTArray<Segment>& segments() {
3890 return fSegments;
3891 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003892
caryclark@google.com235f56a2012-09-14 14:19:30 +00003893 void setOperand(bool isOp) {
3894 fOperand = isOp;
3895 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00003896
caryclark@google.com4eeda372012-12-06 21:47:48 +00003897 void setOppXor(bool isOppXor) {
3898 fOppXor = isOppXor;
3899 int segmentCount = fSegments.count();
3900 for (int test = 0; test < segmentCount; ++test) {
3901 fSegments[test].setOppXor(isOppXor);
3902 }
3903 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003904
caryclark@google.com235f56a2012-09-14 14:19:30 +00003905 void setXor(bool isXor) {
3906 fXor = isXor;
3907 }
3908
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003909 void sortSegments() {
3910 int segmentCount = fSegments.count();
3911 fSortedSegments.setReserve(segmentCount);
3912 for (int test = 0; test < segmentCount; ++test) {
3913 *fSortedSegments.append() = &fSegments[test];
3914 }
3915 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
3916 fFirstSorted = 0;
3917 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003918
caryclark@google.comf839c032012-10-26 21:03:50 +00003919 const SkPoint& start() const {
3920 return fSegments.front().pts()[0];
3921 }
3922
3923 void toPath(PathWrapper& path) const {
3924 int segmentCount = fSegments.count();
3925 const SkPoint& pt = fSegments.front().pts()[0];
3926 path.deferredMove(pt);
3927 for (int test = 0; test < segmentCount; ++test) {
3928 fSegments[test].addCurveTo(0, 1, path, true);
3929 }
3930 path.close();
3931 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003932
caryclark@google.comf839c032012-10-26 21:03:50 +00003933 void toPartialBackward(PathWrapper& path) const {
3934 int segmentCount = fSegments.count();
3935 for (int test = segmentCount - 1; test >= 0; --test) {
3936 fSegments[test].addCurveTo(1, 0, path, true);
3937 }
3938 }
3939
3940 void toPartialForward(PathWrapper& path) const {
3941 int segmentCount = fSegments.count();
3942 for (int test = 0; test < segmentCount; ++test) {
3943 fSegments[test].addCurveTo(0, 1, path, true);
3944 }
3945 }
3946
3947#if 0 // FIXME: obsolete, remove
caryclark@google.com15fa1382012-05-07 20:49:36 +00003948 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3949 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003950 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003951 // segments' top, and not a true sort, so it could be ameniable to regular
3952 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003953 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003954 int segmentCount = fSegments.count();
3955 SkASSERT(segmentCount > 0);
3956 int best = -1;
3957 Segment* bestSegment = NULL;
3958 while (++best < segmentCount) {
3959 Segment* testSegment = &fSegments[best];
3960 if (testSegment->done()) {
3961 continue;
3962 }
3963 bestSegment = testSegment;
3964 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003965 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003966 if (!bestSegment) {
3967 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003968 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003969 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003970 for (int test = best + 1; test < segmentCount; ++test) {
3971 Segment* testSegment = &fSegments[test];
3972 if (testSegment->done()) {
3973 continue;
3974 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003975 if (testSegment->bounds().fTop > bestTop) {
3976 continue;
3977 }
3978 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003979 if (bestTop > testTop) {
3980 bestTop = testTop;
3981 bestSegment = testSegment;
3982 }
3983 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003984 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003985 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003986 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003987#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003988
caryclark@google.comf839c032012-10-26 21:03:50 +00003989 Segment* topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003990 int segmentCount = fSortedSegments.count();
3991 SkASSERT(segmentCount > 0);
3992 Segment* bestSegment = NULL;
caryclark@google.comf839c032012-10-26 21:03:50 +00003993 int sortedIndex = fFirstSorted;
3994 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
3995 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003996 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003997 if (sortedIndex == fFirstSorted) {
3998 ++fFirstSorted;
3999 }
4000 continue;
4001 }
4002 SkPoint testXY;
4003 testSegment->activeLeftTop(testXY);
4004 if (testXY.fY < topLeft.fY) {
4005 continue;
4006 }
4007 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
4008 continue;
4009 }
4010 if (bestXY.fY < testXY.fY) {
4011 continue;
4012 }
4013 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004014 continue;
4015 }
4016 bestSegment = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00004017 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004018 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004019 return bestSegment;
4020 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004021
caryclark@google.com24bec792012-08-20 12:43:57 +00004022 Segment* undoneSegment(int& start, int& end) {
4023 int segmentCount = fSegments.count();
4024 for (int test = 0; test < segmentCount; ++test) {
4025 Segment* testSegment = &fSegments[test];
4026 if (testSegment->done()) {
4027 continue;
4028 }
4029 testSegment->undoneSpan(start, end);
4030 return testSegment;
4031 }
4032 return NULL;
4033 }
4034
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004035 int updateSegment(int index, const SkPoint* pts) {
4036 Segment& segment = fSegments[index];
4037 segment.updatePts(pts);
4038 return segment.verb() + 1;
4039 }
4040
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004041#if DEBUG_TEST
4042 SkTArray<Segment>& debugSegments() {
4043 return fSegments;
4044 }
4045#endif
4046
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004047#if DEBUG_DUMP
4048 void dump() {
4049 int i;
4050 const char className[] = "Contour";
4051 const int tab = 4;
4052 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
4053 for (i = 0; i < fSegments.count(); ++i) {
4054 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
4055 className, i);
4056 fSegments[i].dump();
4057 }
4058 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
4059 tab + sizeof(className), className,
4060 fBounds.fLeft, fBounds.fTop,
4061 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004062 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
4063 className, fContainsIntercepts);
4064 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
4065 className, fContainsCurves);
4066 }
4067#endif
4068
caryclark@google.com027de222012-07-12 12:52:50 +00004069#if DEBUG_ACTIVE_SPANS
4070 void debugShowActiveSpans() {
4071 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004072 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00004073 }
4074 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004075
4076 void validateActiveSpans() {
4077 for (int index = 0; index < fSegments.count(); ++index) {
4078 fSegments[index].validateActiveSpans();
4079 }
4080 }
caryclark@google.com027de222012-07-12 12:52:50 +00004081#endif
4082
caryclark@google.com729e1c42012-11-21 21:36:34 +00004083#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004084 int debugShowWindingValues(int totalSegments, int ofInterest) {
4085 int count = fSegments.count();
4086 int sum = 0;
4087 for (int index = 0; index < count; ++index) {
4088 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
4089 }
4090 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
4091 return sum;
4092 }
4093
4094 static void debugShowWindingValues(SkTDArray<Contour*>& contourList) {
4095 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
4096 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
4097 int ofInterest = 1 << 5 | 1 << 8;
4098 int total = 0;
4099 int index;
4100 for (index = 0; index < contourList.count(); ++index) {
4101 total += contourList[index]->segments().count();
4102 }
4103 int sum = 0;
4104 for (index = 0; index < contourList.count(); ++index) {
4105 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
4106 }
4107 // SkDebugf("%s total=%d\n", __FUNCTION__, sum);
4108 }
4109#endif
4110
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004111protected:
4112 void setBounds() {
4113 int count = fSegments.count();
4114 if (count == 0) {
4115 SkDebugf("%s empty contour\n", __FUNCTION__);
4116 SkASSERT(0);
4117 // FIXME: delete empty contour?
4118 return;
4119 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004120 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004121 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004122 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004123 }
4124 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004125
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004126private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004127 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004128 SkTDArray<Segment*> fSortedSegments;
4129 int fFirstSorted;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004130 SkTDArray<Coincidence> fCoincidences;
4131 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004132 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004133 bool fContainsIntercepts;
4134 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004135 bool fOperand; // true for the second argument to a binary operator
4136 bool fXor;
caryclark@google.com4eeda372012-12-06 21:47:48 +00004137 bool fOppXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004138#if DEBUG_DUMP
4139 int fID;
4140#endif
4141};
4142
4143class EdgeBuilder {
4144public:
4145
caryclark@google.comf839c032012-10-26 21:03:50 +00004146EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
4147 : fPath(path.nativePath())
4148 , fContours(contours)
4149{
4150 init();
4151}
4152
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004153EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00004154 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004155 , fContours(contours)
4156{
caryclark@google.comf839c032012-10-26 21:03:50 +00004157 init();
4158}
4159
4160void init() {
4161 fCurrentContour = NULL;
4162 fOperand = false;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004163 fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004164#if DEBUG_DUMP
4165 gContourID = 0;
4166 gSegmentID = 0;
4167#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00004168 fSecondHalf = preFetch();
4169}
4170
4171void addOperand(const SkPath& path) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004172 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
4173 fPathVerbs.pop();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004174 fPath = &path;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004175 fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004176 preFetch();
4177}
4178
4179void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004180 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004181 complete();
4182 if (fCurrentContour && !fCurrentContour->segments().count()) {
4183 fContours.pop_back();
4184 }
4185 // correct pointers in contours since fReducePts may have moved as it grew
4186 int cIndex = 0;
4187 int extraCount = fExtra.count();
4188 SkASSERT(extraCount == 0 || fExtra[0] == -1);
4189 int eIndex = 0;
4190 int rIndex = 0;
4191 while (++eIndex < extraCount) {
4192 int offset = fExtra[eIndex];
4193 if (offset < 0) {
4194 ++cIndex;
4195 continue;
4196 }
4197 fCurrentContour = &fContours[cIndex];
4198 rIndex += fCurrentContour->updateSegment(offset - 1,
4199 &fReducePts[rIndex]);
4200 }
4201 fExtra.reset(); // we're done with this
4202}
4203
4204ShapeOpMask xorMask() const {
caryclark@google.com729e1c42012-11-21 21:36:34 +00004205 return fXorMask[fOperand];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004206}
4207
4208protected:
4209
4210void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004211 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004212 fCurrentContour->complete();
4213 fCurrentContour = NULL;
4214 }
4215}
4216
caryclark@google.com235f56a2012-09-14 14:19:30 +00004217// FIXME:remove once we can access path pts directly
4218int preFetch() {
4219 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004220 SkPoint pts[4];
4221 SkPath::Verb verb;
4222 do {
4223 verb = iter.next(pts);
4224 *fPathVerbs.append() = verb;
4225 if (verb == SkPath::kMove_Verb) {
4226 *fPathPts.append() = pts[0];
4227 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
4228 fPathPts.append(verb, &pts[1]);
4229 }
4230 } while (verb != SkPath::kDone_Verb);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004231 return fPathVerbs.count() - 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004232}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004233
caryclark@google.com235f56a2012-09-14 14:19:30 +00004234void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004235 SkPath::Verb reducedVerb;
4236 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004237 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004238 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004239 const SkPoint* finalCurveStart = NULL;
4240 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004241 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004242 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
4243 switch (verb) {
4244 case SkPath::kMove_Verb:
4245 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004246 if (!fCurrentContour) {
4247 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004248 fCurrentContour->setOperand(fOperand);
caryclark@google.com729e1c42012-11-21 21:36:34 +00004249 fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004250 *fExtra.append() = -1; // start new contour
4251 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004252 finalCurveEnd = pointsPtr++;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004253 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004254 case SkPath::kLine_Verb:
4255 // skip degenerate points
4256 if (pointsPtr[-1].fX != pointsPtr[0].fX
4257 || pointsPtr[-1].fY != pointsPtr[0].fY) {
4258 fCurrentContour->addLine(&pointsPtr[-1]);
4259 }
4260 break;
4261 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00004262
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004263 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
4264 if (reducedVerb == 0) {
4265 break; // skip degenerate points
4266 }
4267 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00004268 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004269 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004270 break;
4271 }
4272 fCurrentContour->addQuad(&pointsPtr[-1]);
4273 break;
4274 case SkPath::kCubic_Verb:
4275 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
4276 if (reducedVerb == 0) {
4277 break; // skip degenerate points
4278 }
4279 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004280 *fExtra.append() =
4281 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004282 break;
4283 }
4284 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004285 *fExtra.append() =
4286 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004287 break;
4288 }
4289 fCurrentContour->addCubic(&pointsPtr[-1]);
4290 break;
4291 case SkPath::kClose_Verb:
4292 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004293 if (finalCurveStart && finalCurveEnd
4294 && *finalCurveStart != *finalCurveEnd) {
4295 *fReducePts.append() = *finalCurveStart;
4296 *fReducePts.append() = *finalCurveEnd;
4297 *fExtra.append() =
4298 fCurrentContour->addLine(fReducePts.end() - 2);
4299 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004300 complete();
caryclark@google.com31143cf2012-11-09 22:14:19 +00004301 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004302 default:
4303 SkDEBUGFAIL("bad verb");
4304 return;
4305 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004306 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004307 pointsPtr += verb;
4308 SkASSERT(fCurrentContour);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004309 nextVerb:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004310 if (verbPtr == endOfFirstHalf) {
4311 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004312 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004313 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004314}
4315
4316private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004317 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004318 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004319 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004320 Contour* fCurrentContour;
4321 SkTArray<Contour>& fContours;
4322 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004323 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com729e1c42012-11-21 21:36:34 +00004324 ShapeOpMask fXorMask[2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004325 int fSecondHalf;
4326 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004327};
4328
4329class Work {
4330public:
4331 enum SegmentType {
4332 kHorizontalLine_Segment = -1,
4333 kVerticalLine_Segment = 0,
4334 kLine_Segment = SkPath::kLine_Verb,
4335 kQuad_Segment = SkPath::kQuad_Verb,
4336 kCubic_Segment = SkPath::kCubic_Verb,
4337 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004338
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004339 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4340 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4341 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004342
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004343 // FIXME: does it make sense to write otherIndex now if we're going to
4344 // fix it up later?
4345 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004346 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004347 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004348
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004349 // Avoid collapsing t values that are close to the same since
4350 // we walk ts to describe consecutive intersections. Since a pair of ts can
4351 // be nearly equal, any problems caused by this should be taken care
4352 // of later.
4353 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004354 int addT(double newT, const Work& other) {
4355 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004356 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004357
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004358 bool advance() {
4359 return ++fIndex < fLast;
4360 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004361
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004362 SkScalar bottom() const {
4363 return bounds().fBottom;
4364 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004365
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004366 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004367 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004368 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004369
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004370 const SkPoint* cubic() const {
4371 return fCubic;
4372 }
4373
4374 void init(Contour* contour) {
4375 fContour = contour;
4376 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004377 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004378 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004379
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004380 bool isAdjacent(const Work& next) {
4381 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4382 }
4383
4384 bool isFirstLast(const Work& next) {
4385 return fContour == next.fContour && fIndex == 0
4386 && next.fIndex == fLast - 1;
4387 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004388
4389 SkScalar left() const {
4390 return bounds().fLeft;
4391 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004392
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004393 void promoteToCubic() {
4394 fCubic[0] = pts()[0];
4395 fCubic[2] = pts()[1];
4396 fCubic[3] = pts()[2];
4397 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4398 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4399 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4400 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4401 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004402
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004403 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004404 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004405 }
4406
4407 SkScalar right() const {
4408 return bounds().fRight;
4409 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004410
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004411 ptrdiff_t segmentIndex() const {
4412 return fIndex;
4413 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004414
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004415 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004416 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004417 SegmentType type = (SegmentType) segment.verb();
4418 if (type != kLine_Segment) {
4419 return type;
4420 }
4421 if (segment.isHorizontal()) {
4422 return kHorizontalLine_Segment;
4423 }
4424 if (segment.isVertical()) {
4425 return kVerticalLine_Segment;
4426 }
4427 return kLine_Segment;
4428 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004429
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004430 bool startAfter(const Work& after) {
4431 fIndex = after.fIndex;
4432 return advance();
4433 }
4434
4435 SkScalar top() const {
4436 return bounds().fTop;
4437 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004438
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004439 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004440 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004441 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004442
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004443 SkScalar x() const {
4444 return bounds().fLeft;
4445 }
4446
4447 bool xFlipped() const {
4448 return x() != pts()[0].fX;
4449 }
4450
4451 SkScalar y() const {
4452 return bounds().fTop;
4453 }
4454
4455 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004456 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004457 }
4458
4459protected:
4460 Contour* fContour;
4461 SkPoint fCubic[4];
4462 int fIndex;
4463 int fLast;
4464};
4465
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004466#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004467static void debugShowLineIntersection(int pts, const Work& wt,
4468 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004469 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004470 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004471 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4472 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4473 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4474 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004475 return;
4476 }
4477 SkPoint wtOutPt, wnOutPt;
4478 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4479 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004480 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004481 __FUNCTION__,
4482 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4483 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4484 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004485 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004486 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004487 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004488 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4489 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4490 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004491 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4492 }
4493 SkDebugf("\n");
4494}
4495
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004496static void debugShowQuadLineIntersection(int pts, const Work& wt,
4497 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4498 if (!pts) {
4499 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00004500 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004501 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4502 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004503 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004504 return;
4505 }
4506 SkPoint wtOutPt, wnOutPt;
4507 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4508 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4509 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4510 __FUNCTION__,
4511 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4512 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4513 wtOutPt.fX, wtOutPt.fY);
4514 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004515 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
4516 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004517 }
4518 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4519 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4520 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4521 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004522 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
4523 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004524 }
4525 SkDebugf("\n");
4526}
4527
caryclark@google.coma461ff02012-10-11 12:54:23 +00004528static void debugShowQuadIntersection(int pts, const Work& wt,
4529 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4530 if (!pts) {
4531 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
4532 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
4533 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00004534 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00004535 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004536 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00004537 return;
4538 }
4539 SkPoint wtOutPt, wnOutPt;
4540 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4541 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4542 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4543 __FUNCTION__,
4544 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4545 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4546 wtOutPt.fX, wtOutPt.fY);
4547 if (pts == 2) {
4548 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
4549 }
4550 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4551 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4552 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
4553 wnOutPt.fX, wnOutPt.fY);
4554 if (pts == 2) {
4555 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004556 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004557 SkDebugf("\n");
4558}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004559#else
4560static void debugShowLineIntersection(int , const Work& ,
4561 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004562}
caryclark@google.coma461ff02012-10-11 12:54:23 +00004563
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004564static void debugShowQuadLineIntersection(int , const Work& ,
4565 const Work& , const double [2], const double [2]) {
4566}
4567
caryclark@google.coma461ff02012-10-11 12:54:23 +00004568static void debugShowQuadIntersection(int , const Work& ,
4569 const Work& , const double [2], const double [2]) {
4570}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004571#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004572
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004573static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004574
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004575 if (test != next) {
4576 if (test->bounds().fBottom < next->bounds().fTop) {
4577 return false;
4578 }
4579 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
4580 return true;
4581 }
4582 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004583 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004584 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004585 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004586 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004587 Work wn;
4588 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004589 if (test == next && !wn.startAfter(wt)) {
4590 continue;
4591 }
4592 do {
4593 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
4594 continue;
4595 }
4596 int pts;
4597 Intersections ts;
4598 bool swap = false;
4599 switch (wt.segmentType()) {
4600 case Work::kHorizontalLine_Segment:
4601 swap = true;
4602 switch (wn.segmentType()) {
4603 case Work::kHorizontalLine_Segment:
4604 case Work::kVerticalLine_Segment:
4605 case Work::kLine_Segment: {
4606 pts = HLineIntersect(wn.pts(), wt.left(),
4607 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004608 debugShowLineIntersection(pts, wt, wn,
4609 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004610 break;
4611 }
4612 case Work::kQuad_Segment: {
4613 pts = HQuadIntersect(wn.pts(), wt.left(),
4614 wt.right(), wt.y(), wt.xFlipped(), ts);
4615 break;
4616 }
4617 case Work::kCubic_Segment: {
4618 pts = HCubicIntersect(wn.pts(), wt.left(),
4619 wt.right(), wt.y(), wt.xFlipped(), ts);
4620 break;
4621 }
4622 default:
4623 SkASSERT(0);
4624 }
4625 break;
4626 case Work::kVerticalLine_Segment:
4627 swap = true;
4628 switch (wn.segmentType()) {
4629 case Work::kHorizontalLine_Segment:
4630 case Work::kVerticalLine_Segment:
4631 case Work::kLine_Segment: {
4632 pts = VLineIntersect(wn.pts(), wt.top(),
4633 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004634 debugShowLineIntersection(pts, wt, wn,
4635 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004636 break;
4637 }
4638 case Work::kQuad_Segment: {
4639 pts = VQuadIntersect(wn.pts(), wt.top(),
4640 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4641 break;
4642 }
4643 case Work::kCubic_Segment: {
4644 pts = VCubicIntersect(wn.pts(), wt.top(),
4645 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4646 break;
4647 }
4648 default:
4649 SkASSERT(0);
4650 }
4651 break;
4652 case Work::kLine_Segment:
4653 switch (wn.segmentType()) {
4654 case Work::kHorizontalLine_Segment:
4655 pts = HLineIntersect(wt.pts(), wn.left(),
4656 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004657 debugShowLineIntersection(pts, wt, wn,
4658 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004659 break;
4660 case Work::kVerticalLine_Segment:
4661 pts = VLineIntersect(wt.pts(), wn.top(),
4662 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004663 debugShowLineIntersection(pts, wt, wn,
4664 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004665 break;
4666 case Work::kLine_Segment: {
4667 pts = LineIntersect(wt.pts(), wn.pts(), ts);
4668 debugShowLineIntersection(pts, wt, wn,
4669 ts.fT[1], ts.fT[0]);
4670 break;
4671 }
4672 case Work::kQuad_Segment: {
4673 swap = true;
4674 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004675 debugShowQuadLineIntersection(pts, wn, wt,
4676 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004677 break;
4678 }
4679 case Work::kCubic_Segment: {
4680 swap = true;
4681 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
4682 break;
4683 }
4684 default:
4685 SkASSERT(0);
4686 }
4687 break;
4688 case Work::kQuad_Segment:
4689 switch (wn.segmentType()) {
4690 case Work::kHorizontalLine_Segment:
4691 pts = HQuadIntersect(wt.pts(), wn.left(),
4692 wn.right(), wn.y(), wn.xFlipped(), ts);
4693 break;
4694 case Work::kVerticalLine_Segment:
4695 pts = VQuadIntersect(wt.pts(), wn.top(),
4696 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4697 break;
4698 case Work::kLine_Segment: {
4699 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004700 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004701 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004702 break;
4703 }
4704 case Work::kQuad_Segment: {
4705 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004706 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004707 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004708 break;
4709 }
4710 case Work::kCubic_Segment: {
4711 wt.promoteToCubic();
4712 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
4713 break;
4714 }
4715 default:
4716 SkASSERT(0);
4717 }
4718 break;
4719 case Work::kCubic_Segment:
4720 switch (wn.segmentType()) {
4721 case Work::kHorizontalLine_Segment:
4722 pts = HCubicIntersect(wt.pts(), wn.left(),
4723 wn.right(), wn.y(), wn.xFlipped(), ts);
4724 break;
4725 case Work::kVerticalLine_Segment:
4726 pts = VCubicIntersect(wt.pts(), wn.top(),
4727 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4728 break;
4729 case Work::kLine_Segment: {
4730 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
4731 break;
4732 }
4733 case Work::kQuad_Segment: {
4734 wn.promoteToCubic();
4735 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
4736 break;
4737 }
4738 case Work::kCubic_Segment: {
4739 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4740 break;
4741 }
4742 default:
4743 SkASSERT(0);
4744 }
4745 break;
4746 default:
4747 SkASSERT(0);
4748 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004749 if (!foundCommonContour && pts > 0) {
4750 test->addCross(next);
4751 next->addCross(test);
4752 foundCommonContour = true;
4753 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004754 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004755 if (pts == 2) {
4756 if (wn.segmentType() <= Work::kLine_Segment
4757 && wt.segmentType() <= Work::kLine_Segment) {
4758 wt.addCoincident(wn, ts, swap);
4759 continue;
4760 }
4761 if (wn.segmentType() == Work::kQuad_Segment
4762 && wt.segmentType() == Work::kQuad_Segment
4763 && ts.coincidentUsed() == 2) {
4764 wt.addCoincident(wn, ts, swap);
4765 continue;
4766 }
4767
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004768 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004769 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004770 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4771 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004772 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4773 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004774 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4775 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004776 }
4777 } while (wn.advance());
4778 } while (wt.advance());
4779 return true;
4780}
4781
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004782// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004783// see if coincidence is formed by clipping non-concident segments
caryclark@google.com4eeda372012-12-06 21:47:48 +00004784static void coincidenceCheck(SkTDArray<Contour*>& contourList, int total) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004785 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004786 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004787 Contour* contour = contourList[cIndex];
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004788 contour->resolveCoincidence(contourList);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004789 }
4790 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4791 Contour* contour = contourList[cIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004792 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004793 }
4794}
4795
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004796// project a ray from the top of the contour up and see if it hits anything
4797// note: when we compute line intersections, we keep track of whether
4798// two contours touch, so we need only look at contours not touching this one.
4799// OPTIMIZATION: sort contourList vertically to avoid linear walk
4800static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com31143cf2012-11-09 22:14:19 +00004801 const Segment* current, int index, int endIndex, bool opp) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004802 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004803 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004804 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00004805 const Segment* test = NULL;
4806 int tIndex;
4807 double tHit;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004808 for (int cTest = 0; cTest < contourCount; ++cTest) {
4809 Contour* contour = contourList[cTest];
caryclark@google.com7fce0de2012-11-29 14:31:50 +00004810 if ((contour->operand() ^ current->operand()) != opp) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004811 continue;
4812 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004813 if (basePt.fY < contour->bounds().fTop) {
4814 continue;
4815 }
4816 if (bestY > contour->bounds().fBottom) {
4817 continue;
4818 }
caryclark@google.com47580692012-07-23 12:14:49 +00004819 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
4820 if (next) {
4821 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004822 }
caryclark@google.com47580692012-07-23 12:14:49 +00004823 }
4824 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00004825 return 0;
4826 }
4827 int winding, windValue;
4828 // If the ray hit the end of a span, we need to construct the wheel of
4829 // angles to find the span closest to the ray -- even if there are just
4830 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004831 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004832 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00004833 SkTDArray<Angle> angles;
4834 int end = test->nextSpan(tIndex, 1);
4835 if (end < 0) {
4836 end = test->nextSpan(tIndex, -1);
4837 }
4838 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00004839 SkASSERT(angles.count() > 0);
4840 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
4841#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00004842 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00004843#endif
4844 return 0;
4845 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004846 test->buildAngles(tIndex, angles, false);
caryclark@google.com47580692012-07-23 12:14:49 +00004847 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00004848 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00004849 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004850 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00004851 // hour after 6 o'clock
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004852 (void) Segment::SortAngles(angles, sorted);
caryclark@google.com47580692012-07-23 12:14:49 +00004853#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004854 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00004855#endif
4856 // walk the sorted angle fan to find the lowest angle
4857 // above the base point. Currently, the first angle in the sorted array
4858 // is 12 noon or an earlier hour (the next counterclockwise)
4859 int count = sorted.count();
4860 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004861 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00004862 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004863 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00004864 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00004865 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004866 if (angle->unsortable()) {
4867 continue;
4868 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004869 if (baseMatches && angle->isHorizontal()) {
4870 continue;
4871 }
4872 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00004873 test = angle->segment();
4874 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
4875 const SkPoint* pts = test->pts();
4876 indexDx = pts[2].fX - pts[1].fX - indexDx;
4877 }
caryclark@google.com47580692012-07-23 12:14:49 +00004878 if (indexDx < 0) {
4879 left = index;
4880 } else if (indexDx > 0) {
4881 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00004882 int previous = index - 1;
4883 if (previous < 0) {
4884 previous = count - 1;
4885 }
4886 const Angle* prev = sorted[previous];
4887 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
4888#if DEBUG_SORT
4889 SkDebugf("%s use prev\n", __FUNCTION__);
4890#endif
4891 right = previous;
4892 }
caryclark@google.com47580692012-07-23 12:14:49 +00004893 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004894 } else {
4895 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004896 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004897 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004898 if (left < 0 && right < 0) {
4899 left = mid;
4900 }
caryclark@google.com47580692012-07-23 12:14:49 +00004901 SkASSERT(left >= 0 || right >= 0);
4902 if (left < 0) {
4903 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004904 } else if (left >= 0 && mid >= 0 && right >= 0
4905 && sorted[mid]->sign() == sorted[right]->sign()) {
4906 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004907 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004908 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004909 test = angle->segment();
4910 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004911 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004912 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004913#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004914 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4915 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004916#endif
4917 } else {
4918 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004919 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004920 windValue = test->windValue(tIndex);
4921#if DEBUG_WINDING
4922 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4923 windValue);
4924#endif
4925 }
4926 // see if a + change in T results in a +/- change in X (compute x'(T))
4927 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004928 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4929 const SkPoint* pts = test->pts();
4930 dx = pts[2].fX - pts[1].fX - dx;
4931 }
caryclark@google.com47580692012-07-23 12:14:49 +00004932#if DEBUG_WINDING
4933 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4934#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004935 SkASSERT(dx != 0);
4936 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004937 winding += dx > 0 ? -windValue : windValue;
4938#if DEBUG_WINDING
4939 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4940#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004941 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004942 return winding;
4943}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004944
caryclark@google.com24bec792012-08-20 12:43:57 +00004945static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4946 int contourCount = contourList.count();
4947 Segment* result;
4948 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4949 Contour* contour = contourList[cIndex];
4950 result = contour->undoneSegment(start, end);
4951 if (result) {
4952 return result;
4953 }
4954 }
4955 return NULL;
4956}
4957
4958
4959
caryclark@google.com31143cf2012-11-09 22:14:19 +00004960static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004961 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004962 Span* span;
4963 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004964 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4965 Segment* segment = backPtr.fOther;
4966 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004967 SkTDArray<Angle> angles;
4968 int done = 0;
4969 if (segment->activeAngle(tIndex, done, angles)) {
4970 Angle* last = angles.end() - 1;
4971 tIndex = last->start();
4972 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00004973 #if TRY_ROTATE
4974 *chase.insert(0) = span;
4975 #else
4976 *chase.append() = span;
4977 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004978 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004979 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004980 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004981 continue;
4982 }
4983 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004984 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004985#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004986 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004987#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004988 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004989 continue;
4990 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004991 // find first angle, initialize winding to computed fWindSum
4992 int firstIndex = -1;
4993 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004994 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004995 do {
4996 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004997 segment = angle->segment();
4998 winding = segment->windSum(angle);
4999 } while (winding == SK_MinS32);
5000 int spanWinding = segment->spanSign(angle->start(), angle->end());
5001 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005002 SkDebugf("%s winding=%d spanWinding=%d\n",
5003 __FUNCTION__, winding, spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00005004 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005005 // turn span winding into contour winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005006 if (spanWinding * winding < 0) {
5007 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005008 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005009 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005010 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005011 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005012 // we care about first sign and whether wind sum indicates this
5013 // edge is inside or outside. Maybe need to pass span winding
5014 // or first winding or something into this function?
5015 // advance to first undone angle, then return it and winding
5016 // (to set whether edges are active or not)
5017 int nextIndex = firstIndex + 1;
5018 int angleCount = sorted.count();
5019 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005020 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00005021 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00005022 do {
5023 SkASSERT(nextIndex != firstIndex);
5024 if (nextIndex == angleCount) {
5025 nextIndex = 0;
5026 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005027 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00005028 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005029 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00005030 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005031 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00005032 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
5033 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005034 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005035 tIndex = angle->start();
5036 endIndex = angle->end();
5037 int lesser = SkMin32(tIndex, endIndex);
5038 const Span& nextSpan = segment->span(lesser);
5039 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005040#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00005041 // FIXME: this be wrong. assign startWinding if edge is in
5042 // same direction. If the direction is opposite, winding to
5043 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00005044 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005045 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005046 }
caryclark@google.com59823f72012-08-09 18:17:47 +00005047 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005048#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005049 break;
5050 }
5051 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005052 #if TRY_ROTATE
5053 *chase.insert(0) = span;
5054 #else
5055 *chase.append() = span;
5056 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005057 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005058 }
5059 return NULL;
5060}
5061
caryclark@google.com027de222012-07-12 12:52:50 +00005062#if DEBUG_ACTIVE_SPANS
5063static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005064 int index;
5065 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00005066 contourList[index]->debugShowActiveSpans();
5067 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005068 for (index = 0; index < contourList.count(); ++ index) {
5069 contourList[index]->validateActiveSpans();
5070 }
caryclark@google.com027de222012-07-12 12:52:50 +00005071}
5072#endif
5073
caryclark@google.com27c449a2012-07-27 18:26:38 +00005074static bool windingIsActive(int winding, int spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005075 // FIXME: !spanWinding test must be superflorous, true?
caryclark@google.com27c449a2012-07-27 18:26:38 +00005076 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
5077 && (!winding || !spanWinding || winding == -spanWinding);
5078}
5079
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005080static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comf839c032012-10-26 21:03:50 +00005081 int& endIndex, SkPoint& topLeft) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005082 Segment* result;
5083 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00005084 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005085 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00005086 Segment* topStart = NULL;
5087 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5088 Contour* contour = contourList[cIndex];
5089 const Bounds& bounds = contour->bounds();
5090 if (bounds.fBottom < topLeft.fY) {
5091 continue;
5092 }
5093 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
5094 continue;
5095 }
5096 Segment* test = contour->topSortableSegment(topLeft, bestXY);
5097 if (test) {
5098 topStart = test;
5099 }
5100 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005101 if (!topStart) {
5102 return NULL;
5103 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005104 topLeft = bestXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005105 result = topStart->findTop(index, endIndex);
5106 } while (!result);
5107 return result;
5108}
caryclark@google.com31143cf2012-11-09 22:14:19 +00005109
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005110static int updateWindings(const Segment* current, int index, int endIndex, int& spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005111 int lesser = SkMin32(index, endIndex);
5112 spanWinding = current->spanSign(index, endIndex);
5113 int winding = current->windSum(lesser);
5114 bool inner = useInnerWinding(winding - spanWinding, winding);
5115#if DEBUG_WINDING
5116 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
5117 " inner=%d result=%d\n",
5118 __FUNCTION__, current->debugID(), current->t(lesser),
5119 spanWinding, winding, SkSign32(index - endIndex),
5120 useInnerWinding(winding - spanWinding, winding),
5121 inner ? winding - spanWinding : winding);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005122#endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005123 if (inner) {
5124 winding -= spanWinding;
5125 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005126 return winding;
5127}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005128
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005129// Each segment may have an inside or an outside. Segments contained within
5130// winding may have insides on either side, and form a contour that should be
5131// ignored. Segments that are coincident with opposing direction segments may
5132// have outsides on either side, and should also disappear.
5133// 'Normal' segments will have one inside and one outside. Subsequent connections
5134// when winding should follow the intersection direction. If more than one edge
5135// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005136 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00005137 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005138// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005139static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005140 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005141 bool unsortable = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005142 bool closable = true;
5143 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com15fa1382012-05-07 20:49:36 +00005144 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005145 int index, endIndex;
caryclark@google.com31143cf2012-11-09 22:14:19 +00005146 // iterates while top is unsortable
caryclark@google.comf839c032012-10-26 21:03:50 +00005147 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005148 if (!current) {
5149 break;
5150 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005151 int contourWinding;
5152 if (firstContour) {
5153 contourWinding = 0;
5154 firstContour = false;
5155 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00005156 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005157 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00005158 if (sumWinding == SK_MinS32) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00005159 sumWinding = current->computeSum(index, endIndex, false);
caryclark@google.com200c2112012-08-03 15:05:04 +00005160 }
5161 if (sumWinding == SK_MinS32) {
5162 contourWinding = innerContourCheck(contourList, current,
caryclark@google.com31143cf2012-11-09 22:14:19 +00005163 index, endIndex, false);
caryclark@google.com200c2112012-08-03 15:05:04 +00005164 } else {
5165 contourWinding = sumWinding;
5166 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00005167 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
5168 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00005169 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005170 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00005171#if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005172 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n",
5173 __FUNCTION__, sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00005174 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00005175#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005176 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005177#if DEBUG_WINDING
5178 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
5179#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005180 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005181 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005182 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005183 // FIXME: needs work. While it works in limited situations, it does
5184 // not always compute winding correctly. Active should be removed and instead
5185 // the initial winding should be correctly passed in so that if the
5186 // inner contour is wound the same way, it never finds an accumulated
5187 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00005188 // other than zero when resolving sorted angles.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005189 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005190 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005191 bool active = windingIsActive(winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005192 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00005193 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
5194 __FUNCTION__, active ? "true" : "false",
5195 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005196 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005197 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005198 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005199 int nextStart = index;
5200 int nextEnd = endIndex;
5201 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005202 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005203 if (!next) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005204 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00005205 && current->verb() != SkPath::kLine_Verb
5206 && !simple.isClosed()) {
5207 current->addCurveTo(index, endIndex, simple, true);
5208 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005209 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005210 break;
5211 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005212 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005213 current = next;
5214 index = nextStart;
5215 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005216 } while (!simple.isClosed()
5217 && ((active && !unsortable) || !current->done()));
5218 if (active) {
5219 if (!simple.isClosed()) {
5220 SkASSERT(unsortable);
5221 int min = SkMin32(index, endIndex);
5222 if (!current->done(min)) {
5223 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.com31143cf2012-11-09 22:14:19 +00005224 current->markDone(SkMin32(index, endIndex),
5225 winding ? winding : spanWinding);
caryclark@google.comf839c032012-10-26 21:03:50 +00005226 }
5227 closable = false;
5228 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005229 simple.close();
5230 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005231 current = findChase(chaseArray, index, endIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005232 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00005233 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005234 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005235 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005236 break;
5237 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005238 winding = updateWindings(current, index, endIndex, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005239 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005240 } while (true);
caryclark@google.comf839c032012-10-26 21:03:50 +00005241 return closable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005242}
5243
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005244// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005245static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005246 Segment* current;
5247 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005248 bool unsortable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00005249 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005250 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005251 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005252 int nextStart = start;
5253 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005254 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00005255 if (!next) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005256 if (simple.hasMove()
5257 && current->verb() != SkPath::kLine_Verb
5258 && !simple.isClosed()) {
5259 current->addCurveTo(start, end, simple, true);
5260 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005261 }
caryclark@google.com24bec792012-08-20 12:43:57 +00005262 break;
5263 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005264 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00005265 current = next;
5266 start = nextStart;
5267 end = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005268 } while (!simple.isClosed());
5269 // FIXME: add unsortable test
5270 if (simple.hasMove()) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005271 simple.close();
5272 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005273 #if DEBUG_ACTIVE_SPANS
5274 debugShowActiveSpans(contourList);
5275 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005276 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005277 return !unsortable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005278}
5279
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005280static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5281 int contourCount = contourList.count();
5282 for (int cTest = 0; cTest < contourCount; ++cTest) {
5283 Contour* contour = contourList[cTest];
5284 contour->fixOtherTIndex();
5285 }
5286}
5287
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005288static void sortSegments(SkTDArray<Contour*>& contourList) {
5289 int contourCount = contourList.count();
5290 for (int cTest = 0; cTest < contourCount; ++cTest) {
5291 Contour* contour = contourList[cTest];
5292 contour->sortSegments();
5293 }
5294}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005295
caryclark@google.com4eeda372012-12-06 21:47:48 +00005296static void makeContourList(SkTArray<Contour>& contours, SkTDArray<Contour*>& list,
5297 bool evenOdd, bool oppEvenOdd) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005298 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005299 if (count == 0) {
5300 return;
5301 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005302 for (int index = 0; index < count; ++index) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00005303 Contour& contour = contours[index];
5304 contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
5305 *list.append() = &contour;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005306 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005307 QSort<Contour>(list.begin(), list.end() - 1);
5308}
5309
caryclark@google.comf839c032012-10-26 21:03:50 +00005310static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005311 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005312}
5313
caryclark@google.comf839c032012-10-26 21:03:50 +00005314 /*
5315 check start and end of each contour
5316 if not the same, record them
5317 match them up
5318 connect closest
5319 reassemble contour pieces into new path
5320 */
5321static void assemble(const PathWrapper& path, PathWrapper& simple) {
5322#if DEBUG_PATH_CONSTRUCTION
5323 SkDebugf("%s\n", __FUNCTION__);
5324#endif
5325 SkTArray<Contour> contours;
5326 EdgeBuilder builder(path, contours);
5327 builder.finish();
5328 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005329 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005330 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00005331 for (outer = 0; outer < count; ++outer) {
5332 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005333 const SkPoint& eStart = eContour.start();
5334 const SkPoint& eEnd = eContour.end();
5335 if (approximatelyEqual(eStart, eEnd)) {
5336 eContour.toPath(simple);
5337 continue;
5338 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005339 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005340 }
5341 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005342 if (count == 0) {
5343 return;
5344 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005345 SkTDArray<int> sLink, eLink;
5346 sLink.setCount(count);
5347 eLink.setCount(count);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005348 SkTDArray<double> sBest, eBest;
5349 sBest.setCount(count);
5350 eBest.setCount(count);
caryclark@google.comf839c032012-10-26 21:03:50 +00005351 int rIndex;
5352 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005353 outer = runs[rIndex];
5354 const Contour& oContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005355 const SkPoint& oStart = oContour.start();
5356 const SkPoint& oEnd = oContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005357 double dx = oEnd.fX - oStart.fX;
5358 double dy = oEnd.fY - oStart.fY;
5359 double dist = dx * dx + dy * dy;
5360 sBest[rIndex] = eBest[rIndex] = dist;
5361 sLink[rIndex] = eLink[rIndex] = rIndex;
5362 }
5363 for (rIndex = 0; rIndex < count - 1; ++rIndex) {
5364 outer = runs[rIndex];
5365 const Contour& oContour = contours[outer];
5366 const SkPoint& oStart = oContour.start();
5367 const SkPoint& oEnd = oContour.end();
5368 double bestStartDist = sBest[rIndex];
5369 double bestEndDist = eBest[rIndex];
5370 for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) {
5371 int inner = runs[iIndex];
5372 const Contour& iContour = contours[inner];
caryclark@google.comf839c032012-10-26 21:03:50 +00005373 const SkPoint& iStart = iContour.start();
5374 const SkPoint& iEnd = iContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005375 double dx = iStart.fX - oStart.fX;
5376 double dy = iStart.fY - oStart.fY;
5377 double dist = dx * dx + dy * dy;
5378 if (bestStartDist > dist) {
5379 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005380 sLink[rIndex] = ~iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005381 sLink[iIndex] = ~rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005382 }
5383 dx = iEnd.fX - oStart.fX;
5384 dy = iEnd.fY - oStart.fY;
5385 dist = dx * dx + dy * dy;
5386 if (bestStartDist > dist) {
5387 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005388 sLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005389 eLink[iIndex] = rIndex;
5390 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005391 dx = iStart.fX - oEnd.fX;
5392 dy = iStart.fY - oEnd.fY;
5393 dist = dx * dx + dy * dy;
5394 if (bestEndDist > dist) {
5395 bestEndDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005396 sLink[iIndex] = rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005397 eLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005398 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005399 dx = iEnd.fX - oEnd.fX;
5400 dy = iEnd.fY - oEnd.fY;
5401 dist = dx * dx + dy * dy;
5402 if (bestEndDist > dist) {
5403 bestEndDist = dist;
5404 eLink[iIndex] = ~rIndex;
5405 eLink[rIndex] = ~iIndex;
5406 }
5407 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005408 }
5409 rIndex = 0;
5410 bool forward = true;
5411 bool first = true;
5412 const SkPoint* startPtr;
5413 int sIndex = sLink[rIndex];
caryclark@google.com0b7da432012-10-31 19:00:20 +00005414 SkASSERT(sIndex != INT_MAX);
5415 sLink[rIndex] = INT_MAX;
5416 int eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005417 if (sIndex < 0) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005418 eIndex = sLink[~sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005419 sLink[~sIndex] = INT_MAX;
5420 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005421 eIndex = eLink[sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005422 eLink[sIndex] = INT_MAX;
5423 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005424 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005425 do {
5426 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005427 outer = runs[rIndex];
5428 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005429 if (first) {
5430 startPtr = &contour.start();
5431 first = false;
5432 simple.deferredMove(startPtr[0]);
5433 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005434 if (forward) {
5435 contour.toPartialForward(simple);
caryclark@google.comf839c032012-10-26 21:03:50 +00005436 } else {
5437 contour.toPartialBackward(simple);
caryclark@google.comf839c032012-10-26 21:03:50 +00005438 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005439 if (sIndex == eIndex) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005440 simple.close();
5441 first = forward = true;
5442 break;
5443 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005444 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005445 eIndex = eLink[rIndex];
5446 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005447 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005448 if (eIndex >= 0) {
5449 SkASSERT(sLink[eIndex] == rIndex);
5450 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005451 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005452 SkASSERT(eLink[~eIndex] == ~rIndex);
5453 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005454 }
5455 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005456 eIndex = sLink[rIndex];
5457 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005458 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005459 if (eIndex >= 0) {
5460 SkASSERT(eLink[eIndex] == rIndex);
5461 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005462 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005463 SkASSERT(sLink[~eIndex] == ~rIndex);
5464 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005465 }
5466 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005467 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005468 if (rIndex < 0) {
5469 forward ^= 1;
5470 rIndex = ~rIndex;
5471 }
5472 } while (true);
5473 for (rIndex = 0; rIndex < count; ++rIndex) {
5474 if (sLink[rIndex] != INT_MAX) {
5475 break;
5476 }
5477 }
5478 } while (rIndex < count);
5479 SkASSERT(first);
5480}
5481
5482void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005483 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00005484 result.reset();
5485 result.setFillType(SkPath::kEvenOdd_FillType);
5486 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005487
5488 // turn path into list of segments
5489 SkTArray<Contour> contours;
5490 // FIXME: add self-intersecting cubics' T values to segment
5491 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00005492 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005493 SkTDArray<Contour*> contourList;
caryclark@google.com4eeda372012-12-06 21:47:48 +00005494 makeContourList(contours, contourList, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005495 Contour** currentPtr = contourList.begin();
5496 if (!currentPtr) {
5497 return;
5498 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005499 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005500 // find all intersections between segments
5501 do {
5502 Contour** nextPtr = currentPtr;
5503 Contour* current = *currentPtr++;
5504 Contour* next;
5505 do {
5506 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005507 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005508 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005509 // eat through coincident edges
caryclark@google.com4eeda372012-12-06 21:47:48 +00005510 coincidenceCheck(contourList, 0);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00005511 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005512 sortSegments(contourList);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005513#if DEBUG_ACTIVE_SPANS
5514 debugShowActiveSpans(contourList);
5515#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005516 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005517 if (builder.xorMask() == kWinding_Mask
5518 ? !bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00005519 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005520 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00005521 SkPath temp;
5522 temp.setFillType(SkPath::kEvenOdd_FillType);
5523 PathWrapper assembled(temp);
5524 assemble(simple, assembled);
5525 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00005526 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005527}
5528