blob: b3072f3eaec4b3c96f79f06a972d1d878b46792c [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 }
skia.committer@gmail.comd21444a2012-12-07 02:01:25 +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) {
826 SkASSERT(outerWinding != innerWinding);
827 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.com31143cf2012-11-09 22:14:19 +0000839static const bool gOpLookup[][2][2] = {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000840 // ==0 !=0
841 // b a b a
842 {{true , false}, {false, true }}, // a - b
843 {{false, false}, {true , true }}, // a & b
844 {{true , true }, {false, false}}, // a | b
845 {{true , true }, {true , true }}, // a ^ b
846};
847
caryclark@google.com7fce0de2012-11-29 14:31:50 +0000848static bool isActiveOp(bool angleIsOp, int otherNonZero, ShapeOp op) {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000849 return gOpLookup[op][otherNonZero][angleIsOp];
caryclark@google.com235f56a2012-09-14 14:19:30 +0000850}
851
caryclark@google.comf839c032012-10-26 21:03:50 +0000852// wrap path to keep track of whether the contour is initialized and non-empty
853class PathWrapper {
854public:
855 PathWrapper(SkPath& path)
856 : fPathPtr(&path)
857 {
858 init();
859 }
860
861 void close() {
862 if (!fHasMove) {
863 return;
864 }
865 bool callClose = isClosed();
866 lineTo();
867 if (fEmpty) {
868 return;
869 }
870 if (callClose) {
871 #if DEBUG_PATH_CONSTRUCTION
872 SkDebugf("path.close();\n");
873 #endif
874 fPathPtr->close();
875 }
876 init();
877 }
878
879 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
880 lineTo();
881 moveTo();
882#if DEBUG_PATH_CONSTRUCTION
883 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
884 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
885#endif
886 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
887 fDefer[0] = fDefer[1] = pt3;
888 fEmpty = false;
889 }
890
891 void deferredLine(const SkPoint& pt) {
892 if (pt == fDefer[1]) {
893 return;
894 }
895 if (changedSlopes(pt)) {
896 lineTo();
897 fDefer[0] = fDefer[1];
898 }
899 fDefer[1] = pt;
900 }
901
902 void deferredMove(const SkPoint& pt) {
903 fMoved = true;
904 fHasMove = true;
905 fEmpty = true;
906 fDefer[0] = fDefer[1] = pt;
907 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000908
caryclark@google.comf839c032012-10-26 21:03:50 +0000909 void deferredMoveLine(const SkPoint& pt) {
910 if (!fHasMove) {
911 deferredMove(pt);
912 }
913 deferredLine(pt);
914 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000915
caryclark@google.comf839c032012-10-26 21:03:50 +0000916 bool hasMove() const {
917 return fHasMove;
918 }
919
920 void init() {
921 fEmpty = true;
922 fHasMove = false;
923 fMoved = false;
924 }
925
926 bool isClosed() const {
927 return !fEmpty && fFirstPt == fDefer[1];
928 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000929
caryclark@google.comf839c032012-10-26 21:03:50 +0000930 void lineTo() {
931 if (fDefer[0] == fDefer[1]) {
932 return;
933 }
934 moveTo();
935 fEmpty = false;
936#if DEBUG_PATH_CONSTRUCTION
937 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
938#endif
939 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
940 fDefer[0] = fDefer[1];
941 }
942
943 const SkPath* nativePath() const {
944 return fPathPtr;
945 }
946
947 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
948 lineTo();
949 moveTo();
950#if DEBUG_PATH_CONSTRUCTION
951 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
952 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
953#endif
954 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
955 fDefer[0] = fDefer[1] = pt2;
956 fEmpty = false;
957 }
958
959protected:
960 bool changedSlopes(const SkPoint& pt) const {
961 if (fDefer[0] == fDefer[1]) {
962 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000963 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000964 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
965 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
966 SkScalar lineDx = pt.fX - fDefer[1].fX;
967 SkScalar lineDy = pt.fY - fDefer[1].fY;
968 return deferDx * lineDy != deferDy * lineDx;
969 }
970
971 void moveTo() {
972 if (!fMoved) {
973 return;
974 }
975 fFirstPt = fDefer[0];
976#if DEBUG_PATH_CONSTRUCTION
977 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
978#endif
979 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
980 fMoved = false;
981 }
982
983private:
984 SkPath* fPathPtr;
985 SkPoint fDefer[2];
986 SkPoint fFirstPt;
987 bool fEmpty;
988 bool fHasMove;
989 bool fMoved;
990};
991
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000992class Segment {
993public:
994 Segment() {
995#if DEBUG_DUMP
996 fID = ++gSegmentID;
997#endif
998 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +0000999
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001000 bool operator<(const Segment& rh) const {
1001 return fBounds.fTop < rh.fBounds.fTop;
1002 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001003
caryclark@google.com4eeda372012-12-06 21:47:48 +00001004 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001005 if (activeAngleInner(index, done, angles)) {
1006 return true;
1007 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001008 double referenceT = fTs[index].fT;
1009 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001010 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001011 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001012 return true;
1013 }
1014 }
1015 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001016 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001017 return true;
1018 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001019 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001020 return false;
1021 }
1022
caryclark@google.com4eeda372012-12-06 21:47:48 +00001023 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001024 Span* span = &fTs[index];
1025 Segment* other = span->fOther;
1026 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001027 return other->activeAngleInner(oIndex, done, angles);
1028 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001029
caryclark@google.com4eeda372012-12-06 21:47:48 +00001030 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001031 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001032 if (next > 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001033 Span& upSpan = fTs[index];
1034 if (upSpan.fWindValue || upSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001035 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001036 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001037 done++;
1038 } else if (upSpan.fWindSum != SK_MinS32) {
1039 return true;
1040 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001041 } else if (!upSpan.fDone) {
1042 upSpan.fDone = true;
1043 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001044 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001045 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001046 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001047 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001048 if (prev >= 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001049 Span& downSpan = fTs[prev];
1050 if (downSpan.fWindValue || downSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001051 addAngle(angles, index, prev);
1052 if (downSpan.fDone) {
1053 done++;
1054 } else if (downSpan.fWindSum != SK_MinS32) {
1055 return true;
1056 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001057 } else if (!downSpan.fDone) {
1058 downSpan.fDone = true;
1059 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001060 }
1061 }
1062 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001063 }
1064
caryclark@google.comf839c032012-10-26 21:03:50 +00001065 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001066 SkASSERT(!done());
1067 int count = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001068 result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001069 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001070 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001071 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001072 const Span& span = fTs[index];
1073 if (span.fUnsortableStart | lastUnsortable) {
1074 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001075 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001076 if (!span.fDone | !lastDone) {
1077 const SkPoint& xy = xyAtT(index);
1078 if (result.fY < xy.fY) {
1079 goto next;
1080 }
1081 if (result.fY == xy.fY && result.fX < xy.fX) {
1082 goto next;
1083 }
1084 result = xy;
1085 }
1086 next:
1087 lastDone = span.fDone;
1088 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001089 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001090 SkASSERT(result.fY < SK_ScalarMax);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001091 }
1092
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001093 bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, ShapeOp op) {
1094 int sumMiWinding = updateWinding(endIndex, index);
1095 int sumSuWinding = updateOppWinding(endIndex, index);
1096 if (fOperand) {
1097 SkTSwap<int>(sumMiWinding, sumSuWinding);
1098 }
1099 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
1100 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, sumMiWinding, sumSuWinding,
1101 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
1102 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001103
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001104 bool activeOp(int xorMiMask, int xorSuMask,
1105 int index, int endIndex, ShapeOp op,
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001106 int& sumMiWinding, int& sumSuWinding,
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001107 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
1108 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
1109 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
1110 int mask, oppMask;
1111 if (operand()) {
1112 mask = xorSuMask;
1113 oppMask = xorMiMask;
1114 } else {
1115 mask = xorMiMask;
1116 oppMask = xorSuMask;
1117 }
1118 if ((sumWinding & mask) && (maxWinding & mask)) {
1119 return false;
1120 }
1121 int oppCoin = oppSign(index, endIndex) & oppMask;
1122 if (oppCoin) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001123 bool oppCrossZero = !(oppSumWinding & oppMask) || !(oppMaxWinding & oppMask);
1124 bool outside = !(oppSumWinding & oppMask) ^ !(sumWinding & mask);
1125 switch (op) {
1126 case kIntersect_Op:
1127 return !oppCrossZero | !outside;
1128 case kUnion_Op:
1129 return oppCrossZero & !outside;
1130 case kDifference_Op:
1131 return oppCrossZero ? outside : operand();
1132 case kXor_Op:
1133 return !oppCrossZero;
1134 default:
1135 SkASSERT(0);
1136 }
skia.committer@gmail.comd21444a2012-12-07 02:01:25 +00001137
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001138 }
1139 bool oppNonZero = oppMaxWinding & oppMask;
1140 return isActiveOp(operand(), oppNonZero, op);
1141 }
1142
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001143 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001144 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001145 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001146#if DEBUG_ANGLE
caryclark@google.com31143cf2012-11-09 22:14:19 +00001147 if (angles.count() > 1 && !fTs[start].fTiny) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001148 SkPoint angle0Pt, newPt;
1149 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1150 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1151 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1152 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1153 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1154 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001155#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001156 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001157 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001158
caryclark@google.com2ddff932012-08-07 21:25:27 +00001159 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001160 double oEnd) {
1161 int tIndex = -1;
1162 int tCount = fTs.count();
1163 int oIndex = -1;
1164 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001165 do {
1166 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001167 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001168 int tIndexStart = tIndex;
1169 do {
1170 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001171 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001172 int oIndexStart = oIndex;
1173 double nextT;
1174 do {
1175 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001176 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001177 double oNextT;
1178 do {
1179 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001180 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001181 // at this point, spans before and after are at:
1182 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1183 // if tIndexStart == 0, no prior span
1184 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001185
caryclark@google.comcc905052012-07-25 20:59:42 +00001186 // advance the span with zero winding
1187 // if the following span exists (not past the end, non-zero winding)
1188 // connect the two edges
1189 if (!fTs[tIndexStart].fWindValue) {
1190 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1191 #if DEBUG_CONCIDENT
1192 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1193 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001194 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1195 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001196 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001197 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001198 }
1199 if (nextT < 1 && fTs[tIndex].fWindValue) {
1200 #if DEBUG_CONCIDENT
1201 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1202 __FUNCTION__, fID, other.fID, tIndex,
1203 fTs[tIndex].fT, xyAtT(tIndex).fX,
1204 xyAtT(tIndex).fY);
1205 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001206 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001207 }
1208 } else {
1209 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1210 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1211 #if DEBUG_CONCIDENT
1212 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1213 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001214 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1215 other.xyAtT(oIndexStart).fY);
1216 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001217 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001218 }
1219 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1220 #if DEBUG_CONCIDENT
1221 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1222 __FUNCTION__, fID, other.fID, oIndex,
1223 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1224 other.xyAtT(oIndex).fY);
1225 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1226 #endif
1227 }
1228 }
1229 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001230
caryclark@google.comcc905052012-07-25 20:59:42 +00001231 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1232 double oEnd) {
1233 // walk this to outsideTs[0]
1234 // walk other to outsideTs[1]
1235 // if either is > 0, add a pointer to the other, copying adjacent winding
1236 int tIndex = -1;
1237 int oIndex = -1;
1238 double tStart = outsideTs[0];
1239 double oStart = outsideTs[1];
1240 do {
1241 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001242 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001243 do {
1244 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001245 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001246 if (tIndex > 0 || oIndex > 0 || fOperand != other.fOperand) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001247 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001248 }
1249 tStart = fTs[tIndex].fT;
1250 oStart = other.fTs[oIndex].fT;
1251 do {
1252 double nextT;
1253 do {
1254 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001255 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001256 tStart = nextT;
1257 do {
1258 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001259 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001260 oStart = nextT;
caryclark@google.com4eeda372012-12-06 21:47:48 +00001261 if (tStart == 1 && oStart == 1 && fOperand == other.fOperand) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001262 break;
1263 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001264 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001265 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001266 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001267
caryclark@google.com4eeda372012-12-06 21:47:48 +00001268 void addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
1269 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001270 fBounds.setCubicBounds(pts);
1271 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001272
caryclark@google.comf839c032012-10-26 21:03:50 +00001273 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001274 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001275 const SkPoint* ePtr;
1276 int lastT = fTs.count() - 1;
1277 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1278 ePtr = fPts;
1279 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001280 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001281 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1282 ePtr = edge;
1283 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001284 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001285 bool reverse = ePtr == fPts && start != 0;
1286 if (reverse) {
1287 path.deferredMoveLine(ePtr[fVerb]);
1288 switch (fVerb) {
1289 case SkPath::kLine_Verb:
1290 path.deferredLine(ePtr[0]);
1291 break;
1292 case SkPath::kQuad_Verb:
1293 path.quadTo(ePtr[1], ePtr[0]);
1294 break;
1295 case SkPath::kCubic_Verb:
1296 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1297 break;
1298 default:
1299 SkASSERT(0);
1300 }
1301 // return ePtr[0];
1302 } else {
1303 path.deferredMoveLine(ePtr[0]);
1304 switch (fVerb) {
1305 case SkPath::kLine_Verb:
1306 path.deferredLine(ePtr[1]);
1307 break;
1308 case SkPath::kQuad_Verb:
1309 path.quadTo(ePtr[1], ePtr[2]);
1310 break;
1311 case SkPath::kCubic_Verb:
1312 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1313 break;
1314 default:
1315 SkASSERT(0);
1316 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001317 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001318 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001319 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001320 }
1321
caryclark@google.com4eeda372012-12-06 21:47:48 +00001322 void addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
1323 init(pts, SkPath::kLine_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001324 fBounds.set(pts, 2);
1325 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001326
caryclark@google.comf839c032012-10-26 21:03:50 +00001327#if 0
1328 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001329 const SkPoint& pt = xyAtT(tIndex);
1330 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001331 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001332 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001333 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001334 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001335#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001336
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001337 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001338 void addOtherT(int index, double otherT, int otherIndex) {
1339 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001340 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001341 if (precisely_less_than_zero(otherT)) {
1342 otherT = 0;
1343 } else if (precisely_greater_than_one(otherT)) {
1344 otherT = 1;
1345 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001346 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001347 span.fOtherT = otherT;
1348 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001349 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001350
caryclark@google.com4eeda372012-12-06 21:47:48 +00001351 void addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
1352 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001353 fBounds.setQuadBounds(pts);
1354 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001355
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001356 // Defer all coincident edge processing until
1357 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001358
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001359// no need to be tricky; insert in normal T order
1360// resolve overlapping ts when considering coincidence later
1361
1362 // add non-coincident intersection. Resulting edges are sorted in T.
1363 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001364 // FIXME: in the pathological case where there is a ton of intercepts,
1365 // binary search?
1366 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001367 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001368 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001369 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001370 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001371 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001372 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001373 newT = 1;
1374 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001375 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001376 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001377 // OPTIMIZATION: if there are three or more identical Ts, then
1378 // the fourth and following could be further insertion-sorted so
1379 // that all the edges are clockwise or counterclockwise.
1380 // This could later limit segment tests to the two adjacent
1381 // neighbors, although it doesn't help with determining which
1382 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001383 if (newT < fTs[index].fT) {
1384 insertedAt = index;
1385 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001386 }
1387 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001388 Span* span;
1389 if (insertedAt >= 0) {
1390 span = fTs.insert(insertedAt);
1391 } else {
1392 insertedAt = tCount;
1393 span = fTs.append();
1394 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001395 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001396 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001397 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001398 span->fWindSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001399 span->fOppSum = SK_MinS32;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001400 span->fWindValue = 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001401 span->fOppValue = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001402 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001403 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001404 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001405 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001406 span->fUnsortableStart = false;
1407 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001408 if (span - fTs.begin() > 0 && !span[-1].fDone
1409 && !precisely_negative(newT - span[-1].fT)
1410 // && approximately_negative(newT - span[-1].fT)
1411 && xyAtT(&span[-1]) == xyAtT(span)) {
1412 span[-1].fTiny = true;
1413 span[-1].fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001414 if (approximately_negative(newT - span[-1].fT)) {
1415 if (approximately_greater_than_one(newT)) {
1416 span[-1].fUnsortableStart = true;
1417 span[-2].fUnsortableEnd = true;
1418 }
1419 if (approximately_less_than_zero(span[-1].fT)) {
1420 span->fUnsortableStart = true;
1421 span[-1].fUnsortableEnd = true;
1422 }
1423 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001424 ++fDoneSpans;
1425 }
1426 if (fTs.end() - span > 1 && !span->fDone
1427 && !precisely_negative(span[1].fT - newT)
1428 // && approximately_negative(span[1].fT - newT)
1429 && xyAtT(&span[1]) == xyAtT(span)) {
1430 span->fTiny = true;
1431 span->fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001432 if (approximately_negative(span[1].fT - newT)) {
1433 if (approximately_greater_than_one(span[1].fT)) {
1434 span->fUnsortableStart = true;
1435 span[-1].fUnsortableEnd = true;
1436 }
1437 if (approximately_less_than_zero(newT)) {
1438 span[1].fUnsortableStart = true;
1439 span->fUnsortableEnd = true;
1440 }
1441 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001442 ++fDoneSpans;
1443 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001444 return insertedAt;
1445 }
1446
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001447 // set spans from start to end to decrement by one
1448 // note this walks other backwards
1449 // FIMXE: there's probably an edge case that can be constructed where
1450 // two span in one segment are separated by float epsilon on one span but
1451 // not the other, if one segment is very small. For this
1452 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001453 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001454 // sorted? It feels better in such a case to match the span's other span
1455 // pointer since both coincident segments must contain the same spans.
1456 void addTCancel(double startT, double endT, Segment& other,
1457 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001458 SkASSERT(!approximately_negative(endT - startT));
1459 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001460 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001461 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001462 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001463 ++index;
1464 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001465 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001466 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001467 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001468 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001469 Span* test = &fTs[index];
1470 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001471 SkTDArray<double> outsideTs;
1472 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001473 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001474 bool decrement = test->fWindValue && oTest->fWindValue && !binary;
caryclark@google.comcc905052012-07-25 20:59:42 +00001475 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001476 double testT = test->fT;
1477 double oTestT = oTest->fT;
1478 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001479 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001480 if (decrement) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001481 decrementSpan(span);
caryclark@google.com200c2112012-08-03 15:05:04 +00001482 } else if (track && span->fT < 1 && oTestT < 1) {
1483 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001484 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001485 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001486 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001487 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001488 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1489 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1490 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001491 while (approximately_negative(otherTMatchStart - oSpan->fT)
1492 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001493 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001494 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001495 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001496 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001497 other.decrementSpan(oSpan);
1498 } else if (track && oSpan->fT < 1 && testT < 1) {
1499 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001500 }
1501 if (!oIndex) {
1502 break;
1503 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001504 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001505 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001506 test = span;
1507 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001508 } while (!approximately_negative(endT - test->fT));
1509 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001510 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001511 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001512 double tStart = outsideTs[0];
1513 double oStart = outsideTs[1];
1514 addCancelOutsides(tStart, oStart, other, oEndT);
1515 int count = outsideTs.count();
1516 if (count > 2) {
1517 double tStart = outsideTs[count - 2];
1518 double oStart = outsideTs[count - 1];
1519 addCancelOutsides(tStart, oStart, other, oEndT);
1520 }
caryclark@google.com18063442012-07-25 12:05:18 +00001521 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001522 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001523 double tStart = oOutsideTs[0];
1524 double oStart = oOutsideTs[1];
1525 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001526 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001527 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00001528
caryclark@google.com4eeda372012-12-06 21:47:48 +00001529 int bumpCoincidentThis(const Span* oTest, bool opp, int index,
1530 SkTDArray<double>& outsideTs) {
1531 int oWindValue = oTest->fWindValue;
1532 int oOppValue = oTest->fOppValue;
1533 if (opp) {
1534 SkTSwap<int>(oWindValue, oOppValue);
1535 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001536 Span* const test = &fTs[index];
1537 Span* end = test;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001538 const double oStartT = oTest->fT;
1539 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001540 if (bumpSpan(end, oWindValue, oOppValue)) {
1541 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001542 }
1543 end = &fTs[++index];
1544 } while (approximately_negative(end->fT - test->fT));
1545 return index;
1546 }
1547
1548 // because of the order in which coincidences are resolved, this and other
1549 // may not have the same intermediate points. Compute the corresponding
1550 // intermediate T values (using this as the master, other as the follower)
1551 // and walk other conditionally -- hoping that it catches up in the end
caryclark@google.com4eeda372012-12-06 21:47:48 +00001552 int bumpCoincidentOther(const Span* test, double oEndT, int& oIndex,
1553 SkTDArray<double>& oOutsideTs) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001554 Span* const oTest = &fTs[oIndex];
1555 Span* oEnd = oTest;
1556 const double startT = test->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001557 const double oStartT = oTest->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001558 while (!approximately_negative(oEndT - oEnd->fT)
caryclark@google.com4eeda372012-12-06 21:47:48 +00001559 && approximately_negative(oEnd->fT - oStartT)) {
1560 zeroSpan(oEnd);
1561 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001562 oEnd = &fTs[++oIndex];
1563 }
1564 return oIndex;
1565 }
1566
1567 // FIXME: need to test this case:
1568 // contourA has two segments that are coincident
1569 // contourB has two segments that are coincident in the same place
1570 // each ends up with +2/0 pairs for winding count
1571 // since logic below doesn't transfer count (only increments/decrements) can this be
1572 // resolved to +4/0 ?
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001573
1574 // set spans from start to end to increment the greater by one and decrement
1575 // the lesser
caryclark@google.com4eeda372012-12-06 21:47:48 +00001576 void addTCoincident(double startT, double endT, Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001577 SkASSERT(!approximately_negative(endT - startT));
1578 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001579 bool opp = fOperand ^ other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001580 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001581 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001582 ++index;
1583 }
1584 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001585 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001586 ++oIndex;
1587 }
1588 Span* test = &fTs[index];
1589 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001590 SkTDArray<double> outsideTs;
1591 SkTDArray<double> oOutsideTs;
1592 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001593 // if either span has an opposite value and the operands don't match, resolve first
1594 index = bumpCoincidentThis(oTest, opp, index, outsideTs);
1595 oIndex = other.bumpCoincidentOther(test, oEndT, oIndex, oOutsideTs);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001596 test = &fTs[index];
1597 oTest = &other.fTs[oIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001598 } while (!approximately_negative(endT - test->fT));
1599 SkASSERT(approximately_negative(oTest->fT - oEndT));
1600 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001601 if (!done() && outsideTs.count()) {
1602 addCoinOutsides(outsideTs, other, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001603 }
1604 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001605 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001606 }
1607 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001608
caryclark@google.comcc905052012-07-25 20:59:42 +00001609 // FIXME: this doesn't prevent the same span from being added twice
1610 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001611 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001612 int tCount = fTs.count();
1613 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1614 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001615 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001616 break;
1617 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001618 if (approximately_negative(span.fT - t) && span.fOther == &other
1619 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001620#if DEBUG_ADD_T_PAIR
1621 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1622 __FUNCTION__, fID, t, other.fID, otherT);
1623#endif
1624 return;
1625 }
1626 }
caryclark@google.com47580692012-07-23 12:14:49 +00001627#if DEBUG_ADD_T_PAIR
1628 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1629 __FUNCTION__, fID, t, other.fID, otherT);
1630#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001631 int insertedAt = addT(t, &other);
1632 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001633 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001634 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001635 matchWindingValue(insertedAt, t, borrowWind);
1636 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001637 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001638
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001639 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001640 // add edge leading into junction
caryclark@google.com4eeda372012-12-06 21:47:48 +00001641 int min = SkMin32(end, start);
1642 if (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001643 addAngle(angles, end, start);
1644 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001645 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001646 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001647 int tIndex = nextExactSpan(end, step);
caryclark@google.com4eeda372012-12-06 21:47:48 +00001648 min = SkMin32(end, tIndex);
1649 if (tIndex >= 0 && (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001650 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001651 }
1652 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001653
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001654 const Bounds& bounds() const {
1655 return fBounds;
1656 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001657
caryclark@google.com31143cf2012-11-09 22:14:19 +00001658 void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001659 double referenceT = fTs[index].fT;
1660 int lesser = index;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001661 while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand)
1662 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00001663 buildAnglesInner(lesser, angles);
1664 }
1665 do {
1666 buildAnglesInner(index, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001667 } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand)
1668 && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001669 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001670
1671 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1672 Span* span = &fTs[index];
1673 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001674 // if there is only one live crossing, and no coincidence, continue
1675 // in the same direction
1676 // if there is coincidence, the only choice may be to reverse direction
1677 // find edge on either side of intersection
1678 int oIndex = span->fOtherIndex;
1679 // if done == -1, prior span has already been processed
1680 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001681 int next = other->nextExactSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001682 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001683 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001684 next = other->nextExactSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001685 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001686 // add candidate into and away from junction
1687 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001688 }
1689
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001690 int computeSum(int startIndex, int endIndex, bool binary) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001691 SkTDArray<Angle> angles;
1692 addTwoAngles(startIndex, endIndex, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001693 buildAngles(endIndex, angles, false);
caryclark@google.comd1688742012-09-18 20:08:37 +00001694 // OPTIMIZATION: check all angles to see if any have computed wind sum
1695 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001696 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001697 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001698#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001699 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001700#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001701 if (!sortable) {
1702 return SK_MinS32;
1703 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001704 int angleCount = angles.count();
1705 const Angle* angle;
1706 const Segment* base;
1707 int winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001708 int oWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001709 int firstIndex = 0;
1710 do {
1711 angle = sorted[firstIndex];
1712 base = angle->segment();
1713 winding = base->windSum(angle);
1714 if (winding != SK_MinS32) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001715 oWinding = base->oppSum(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001716 break;
1717 }
1718 if (++firstIndex == angleCount) {
1719 return SK_MinS32;
1720 }
1721 } while (true);
1722 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001723 int spanWinding = base->spanSign(angle);
1724 bool inner = useInnerWinding(winding + spanWinding, winding);
1725 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001726 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001727 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001728 inner ? winding + spanWinding : winding);
1729 #endif
1730 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001731 winding += spanWinding;
1732 }
1733 #if DEBUG_SORT
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001734 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001735 #endif
1736 int nextIndex = firstIndex + 1;
1737 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001738 winding -= base->spanSign(angle);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001739 oWinding -= base->oppSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001740 do {
1741 if (nextIndex == angleCount) {
1742 nextIndex = 0;
1743 }
1744 angle = sorted[nextIndex];
1745 Segment* segment = angle->segment();
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001746 bool opp = base->fOperand ^ segment->fOperand;
1747 int maxWinding, oMaxWinding;
1748 int spanSign = segment->spanSign(angle);
1749 int oppoSign = segment->oppSign(angle);
1750 if (opp) {
1751 oMaxWinding = oWinding;
1752 oWinding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001753 maxWinding = winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001754 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001755 winding -= oppoSign;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001756 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001757 } else {
1758 maxWinding = winding;
1759 winding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001760 oMaxWinding = oWinding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001761 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001762 oWinding -= oppoSign;
1763 }
1764 }
1765 if (segment->windSum(angle) == SK_MinS32) {
1766 if (opp) {
1767 if (useInnerWinding(oMaxWinding, oWinding)) {
1768 oMaxWinding = oWinding;
1769 }
1770 if (oppoSign && useInnerWinding(maxWinding, winding)) {
1771 maxWinding = winding;
1772 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001773 (void) segment->markAndChaseWinding(angle, oMaxWinding, maxWinding);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001774 } else {
1775 if (useInnerWinding(maxWinding, winding)) {
1776 maxWinding = winding;
1777 }
1778 if (oppoSign && useInnerWinding(oMaxWinding, oWinding)) {
1779 oMaxWinding = oWinding;
1780 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001781 (void) segment->markAndChaseWinding(angle, maxWinding, binary ? oMaxWinding : 0);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001782 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001783 }
1784 } while (++nextIndex != lastIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001785 int minIndex = SkMin32(startIndex, endIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001786 return windSum(minIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001787 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001788
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001789 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001790 int bestT = -1;
1791 SkScalar top = bounds().fTop;
1792 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001793 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001794 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001795 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001796 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001797 if (fTs[start].fWindValue == 0) {
1798 continue;
1799 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001800 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001801 double startT = fTs[start].fT;
1802 double endT = fTs[end].fT;
1803 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001804 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001805 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001806 // FIXME: always use original and limit results to T values within
1807 // start t and end t.
1808 // OPTIMIZE: use specialty function that intersects ray with curve,
1809 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001810 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1811 false, intersections);
1812 if (pts == 0) {
1813 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001814 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001815 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1816 // if the intersection is edge on, wait for another one
1817 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001818 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001819 for (int index = 0; index < pts; ++index) {
1820 SkPoint pt;
1821 double foundT = intersections.fT[0][index];
1822 double testT = startT + (endT - startT) * foundT;
1823 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1824 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001825 if (fVerb > SkPath::kLine_Verb
1826 && !approximately_less_than_zero(foundT)
1827 && !approximately_greater_than_one(foundT)) {
1828 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1829 if (approximately_zero(dx)) {
1830 continue;
1831 }
1832 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001833 bestY = pt.fY;
1834 bestT = foundT < 1 ? start : end;
1835 hitT = testT;
1836 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001837 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001838 } while (fTs[end].fT != 1);
1839 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001840 }
caryclark@google.com18063442012-07-25 12:05:18 +00001841
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001842 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1843 // if a segment is connected to this one, consider it crossing
1844 int tIndex;
1845 if (fPts[0].fX == basePt.fX) {
1846 tIndex = 0;
1847 do {
1848 const Span& sSpan = fTs[tIndex];
1849 const Segment* sOther = sSpan.fOther;
1850 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1851 continue;
1852 }
1853 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1854 : sOther->fBounds.fRight > basePt.fX) {
1855 return true;
1856 }
1857 } while (fTs[++tIndex].fT == 0);
1858 }
1859 if (fPts[fVerb].fX == basePt.fX) {
1860 tIndex = fTs.count() - 1;
1861 do {
1862 const Span& eSpan = fTs[tIndex];
1863 const Segment* eOther = eSpan.fOther;
1864 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1865 continue;
1866 }
1867 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1868 : eOther->fBounds.fRight > basePt.fX) {
1869 return true;
1870 }
1871 } while (fTs[--tIndex].fT == 1);
1872 }
1873 return false;
1874 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001875
caryclark@google.com4eeda372012-12-06 21:47:48 +00001876 void decrementSpan(Span* span) {
caryclark@google.com18063442012-07-25 12:05:18 +00001877 SkASSERT(span->fWindValue > 0);
1878 if (--(span->fWindValue) == 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001879 if (!span->fOppValue && !span->fDone) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001880 span->fDone = true;
1881 ++fDoneSpans;
1882 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001883 }
1884 }
1885
1886 bool bumpSpan(Span* span, int windDelta, int oppDelta) {
1887 SkASSERT(!span->fDone);
1888 span->fWindValue += windDelta;
1889 SkASSERT(span->fWindValue >= 0);
1890 span->fOppValue += oppDelta;
1891 SkASSERT(span->fOppValue >= 0);
1892 if (fXor) {
1893 span->fWindValue &= 1;
1894 }
1895 if (fOppXor) {
1896 span->fOppValue &= 1;
1897 }
1898 if (!span->fWindValue && !span->fOppValue) {
1899 span->fDone = true;
1900 ++fDoneSpans;
caryclark@google.com18063442012-07-25 12:05:18 +00001901 return true;
1902 }
1903 return false;
1904 }
1905
caryclark@google.com15fa1382012-05-07 20:49:36 +00001906 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001907 SkASSERT(fDoneSpans <= fTs.count());
1908 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001909 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001910
caryclark@google.comf839c032012-10-26 21:03:50 +00001911 bool done(int min) const {
1912 return fTs[min].fDone;
1913 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001914
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001915 bool done(const Angle* angle) const {
1916 return done(SkMin32(angle->start(), angle->end()));
caryclark@google.com47580692012-07-23 12:14:49 +00001917 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001918
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001919 /*
1920 The M and S variable name parts stand for the operators.
1921 Mi stands for Minuend (see wiki subtraction, analogous to difference)
1922 Su stands for Subtrahend
1923 The Opp variable name part designates that the value is for the Opposite operator.
1924 Opposite values result from combining coincident spans.
1925 */
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001926
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001927 Segment* findNextOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
1928 bool& unsortable, ShapeOp op, const int xorMiMask, const int xorSuMask) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001929 const int startIndex = nextStart;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001930 const int endIndex = nextEnd;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001931 SkASSERT(startIndex != endIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001932 const int count = fTs.count();
1933 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
1934 const int step = SkSign32(endIndex - startIndex);
1935 const int end = nextExactSpan(startIndex, step);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001936 SkASSERT(end >= 0);
1937 Span* endSpan = &fTs[end];
1938 Segment* other;
1939 if (isSimple(end)) {
1940 // mark the smaller of startIndex, endIndex done, and all adjacent
1941 // spans with the same T value (but not 'other' spans)
1942 #if DEBUG_WINDING
1943 SkDebugf("%s simple\n", __FUNCTION__);
1944 #endif
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001945 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001946 other = endSpan->fOther;
1947 nextStart = endSpan->fOtherIndex;
1948 double startT = other->fTs[nextStart].fT;
1949 nextEnd = nextStart;
1950 do {
1951 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001952 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00001953 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001954 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1955 return other;
1956 }
1957 // more than one viable candidate -- measure angles to find best
1958 SkTDArray<Angle> angles;
1959 SkASSERT(startIndex - endIndex != 0);
1960 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1961 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001962 buildAngles(end, angles, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001963 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001964 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001965 int angleCount = angles.count();
1966 int firstIndex = findStartingEdge(sorted, startIndex, end);
1967 SkASSERT(firstIndex >= 0);
1968 #if DEBUG_SORT
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001969 debugShowSort(__FUNCTION__, sorted, firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001970 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001971 if (!sortable) {
1972 unsortable = true;
1973 return NULL;
1974 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001975 SkASSERT(sorted[firstIndex]->segment() == this);
1976 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001977 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
1978 sorted[firstIndex]->sign());
caryclark@google.com235f56a2012-09-14 14:19:30 +00001979 #endif
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001980 int sumMiWinding = updateWinding(endIndex, startIndex);
1981 int sumSuWinding = updateOppWinding(endIndex, startIndex);
1982 if (operand()) {
1983 SkTSwap<int>(sumMiWinding, sumSuWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001984 }
1985 int nextIndex = firstIndex + 1;
1986 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1987 const Angle* foundAngle = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001988 bool foundDone = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001989 // iterate through the angle, and compute everyone's winding
caryclark@google.com235f56a2012-09-14 14:19:30 +00001990 Segment* nextSegment;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001991 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001992 SkASSERT(nextIndex != firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001993 if (nextIndex == angleCount) {
1994 nextIndex = 0;
1995 }
1996 const Angle* nextAngle = sorted[nextIndex];
1997 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001998 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
1999 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
2000 nextAngle->end(), op, sumMiWinding, sumSuWinding,
2001 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
2002 if (activeAngle && (!foundAngle || foundDone)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002003 foundAngle = nextAngle;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002004 foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002005 }
2006 if (nextSegment->done()) {
2007 continue;
2008 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002009 if (nextSegment->windSum(nextAngle) != SK_MinS32) {
2010 continue;
2011 }
2012 Span* last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding,
2013 oppSumWinding, activeAngle, nextAngle);
2014 if (last) {
2015 *chase.append() = last;
2016#if DEBUG_WINDING
2017 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2018 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2019#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002020 }
2021 } while (++nextIndex != lastIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002022 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002023 if (!foundAngle) {
2024 return NULL;
2025 }
2026 nextStart = foundAngle->start();
2027 nextEnd = foundAngle->end();
2028 nextSegment = foundAngle->segment();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002029
caryclark@google.com235f56a2012-09-14 14:19:30 +00002030 #if DEBUG_WINDING
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002031 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2032 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002033 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002034 return nextSegment;
2035 }
caryclark@google.com47580692012-07-23 12:14:49 +00002036
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002037 // so the span needs to contain the pairing info found here
2038 // this should include the winding computed for the edge, and
2039 // what edge it connects to, and whether it is discarded
2040 // (maybe discarded == abs(winding) > 1) ?
2041 // only need derivatives for duration of sorting, add a new struct
2042 // for pairings, remove extra spans that have zero length and
2043 // reference an unused other
2044 // for coincident, the last span on the other may be marked done
2045 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002046
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002047 // if loop is exhausted, contour may be closed.
2048 // FIXME: pass in close point so we can check for closure
2049
2050 // given a segment, and a sense of where 'inside' is, return the next
2051 // segment. If this segment has an intersection, or ends in multiple
2052 // segments, find the mate that continues the outside.
2053 // note that if there are multiples, but no coincidence, we can limit
2054 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002055
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002056 // mark found segments as done
2057
caryclark@google.com15fa1382012-05-07 20:49:36 +00002058 // start is the index of the beginning T of this edge
2059 // it is guaranteed to have an end which describes a non-zero length (?)
2060 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002061 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002062 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2063 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002064 const int startIndex = nextStart;
2065 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002066 int outerWinding = winding;
2067 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002068 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002069 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2070 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002071 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002072 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002073 outerWinding = innerWinding;
2074 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002075 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002076 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002077 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2078 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002079 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002080 int end = nextExactSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002081 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002082 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002083 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002084 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002085 // mark the smaller of startIndex, endIndex done, and all adjacent
2086 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002087 #if DEBUG_WINDING
2088 SkDebugf("%s simple\n", __FUNCTION__);
2089 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002090 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002091 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002092 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002093 double startT = other->fTs[nextStart].fT;
2094 nextEnd = nextStart;
2095 do {
2096 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002097 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002098 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00002099 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002100 return other;
2101 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002102 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002103 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002104 SkASSERT(startIndex - endIndex != 0);
2105 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002106 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002107 buildAngles(end, angles, false);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002108 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002109 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002110 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002111 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002112 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002113 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002114 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002115 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002116 if (!sortable) {
2117 unsortable = true;
2118 return NULL;
2119 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002120 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002121 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002122 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002123 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002124 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002125 int nextIndex = firstIndex + 1;
2126 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2127 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002128 // FIXME: found done logic probably fails if there are more than 4
2129 // sorted angles. It should bias towards the first and last undone
2130 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002131 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002132 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002133 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002134 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002135 bool altFlipped = false;
2136 bool foundFlipped = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002137 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002138 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002139 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002140 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002141 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002142 nextIndex = 0;
2143 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002144 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002145 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002146 if (sumWinding) {
2147 lastNonZeroSum = sumWinding;
2148 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002149 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002150 bool nextDone = nextSegment->done(nextAngle);
2151 bool nextTiny = nextSegment->tiny(nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002152 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002153 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002154 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002155 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002156 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2157 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002158 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002159 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002160 if (!active) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002161 // FIXME: shouldn't this call mark and chase done ?
caryclark@google.com59823f72012-08-09 18:17:47 +00002162 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002163 // FIXME: shouldn't this call mark and chase winding ?
caryclark@google.com47580692012-07-23 12:14:49 +00002164 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002165 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002166 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002167 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002168 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002169 return NULL;
2170 }
caryclark@google.com47580692012-07-23 12:14:49 +00002171 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002172 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002173 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002174 foundFlipped = altFlipped;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002175 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002176 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002177 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002178
caryclark@google.com24bec792012-08-20 12:43:57 +00002179 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002180 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002181 if (foundAngle && foundDone2) {
2182 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002183 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002184 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002185 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002186 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002187 foundFlipped = altFlipped;
2188 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002189 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002190 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002191 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002192 }
2193 // if the winding is non-zero, nextAngle does not connect to
2194 // current chain. If we haven't done so already, mark the angle
2195 // as done, record the winding value, and mark connected unambiguous
2196 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002197 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002198 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002199 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002200 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002201 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002202 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002203 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002204 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002205 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002206 }
2207 if (last) {
2208 *chase.append() = last;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002209 #if DEBUG_WINDING
2210 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2211 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2212 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002213 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002214 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002215 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002216 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002217 if (!foundAngle) {
2218 return NULL;
2219 }
2220 nextStart = foundAngle->start();
2221 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002222 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002223 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002224 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2225 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002226 if (winding) {
2227 #if DEBUG_WINDING
2228 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2229 if (foundSum == SK_MinS32) {
2230 SkDebugf("?");
2231 } else {
2232 SkDebugf("%d", foundSum);
2233 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002234 SkDebugf("\n");
2235 #endif
2236 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002237 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002238 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002239 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002240 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002241 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002242 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002243
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002244 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002245 const int startIndex = nextStart;
2246 const int endIndex = nextEnd;
2247 SkASSERT(startIndex != endIndex);
2248 int count = fTs.count();
2249 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2250 : startIndex > 0);
2251 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002252 int end = nextExactSpan(startIndex, step);
caryclark@google.com24bec792012-08-20 12:43:57 +00002253 SkASSERT(end >= 0);
2254 Span* endSpan = &fTs[end];
2255 Segment* other;
2256 markDone(SkMin32(startIndex, endIndex), 1);
2257 if (isSimple(end)) {
2258 #if DEBUG_WINDING
2259 SkDebugf("%s simple\n", __FUNCTION__);
2260 #endif
2261 other = endSpan->fOther;
2262 nextStart = endSpan->fOtherIndex;
2263 double startT = other->fTs[nextStart].fT;
2264 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002265 if ((approximately_less_than_zero(startT) && step < 0)
2266 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002267 step = -step;
2268 SkDEBUGCODE(firstLoop = false;)
2269 }
2270 do {
2271 nextEnd = nextStart;
2272 do {
2273 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002274 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002275 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002276 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2277 break;
2278 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002279 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002280 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002281 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002282 SkDEBUGCODE(firstLoop = false;)
2283 step = -step;
2284 } while (true);
2285 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2286 return other;
2287 }
2288 SkTDArray<Angle> angles;
2289 SkASSERT(startIndex - endIndex != 0);
2290 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2291 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002292 buildAngles(end, angles, false);
caryclark@google.com24bec792012-08-20 12:43:57 +00002293 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002294 bool sortable = SortAngles(angles, sorted);
caryclark@google.com24bec792012-08-20 12:43:57 +00002295 int angleCount = angles.count();
2296 int firstIndex = findStartingEdge(sorted, startIndex, end);
2297 SkASSERT(firstIndex >= 0);
2298 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002299 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002300 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002301 if (!sortable) {
2302 unsortable = true;
2303 return NULL;
2304 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002305 SkASSERT(sorted[firstIndex]->segment() == this);
2306 int nextIndex = firstIndex + 1;
2307 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2308 const Angle* nextAngle;
2309 Segment* nextSegment;
2310 do {
2311 if (nextIndex == angleCount) {
2312 nextIndex = 0;
2313 }
2314 nextAngle = sorted[nextIndex];
2315 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002316 if (!nextSegment->done(nextAngle)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002317 break;
2318 }
2319 if (++nextIndex == lastIndex) {
2320 return NULL;
2321 }
2322 } while (true);
2323 nextStart = nextAngle->start();
2324 nextEnd = nextAngle->end();
2325 return nextSegment;
2326 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002327
2328 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2329 int angleCount = sorted.count();
2330 int firstIndex = -1;
2331 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2332 const Angle* angle = sorted[angleIndex];
2333 if (angle->segment() == this && angle->start() == end &&
2334 angle->end() == start) {
2335 firstIndex = angleIndex;
2336 break;
2337 }
2338 }
2339 return firstIndex;
2340 }
2341
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002342 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com4eeda372012-12-06 21:47:48 +00002343 void findTooCloseToCall() {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002344 int count = fTs.count();
2345 if (count < 3) { // require t=0, x, 1 at minimum
2346 return;
2347 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002348 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002349 int moCount;
2350 Span* match;
2351 Segment* mOther;
2352 do {
2353 match = &fTs[matchIndex];
2354 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002355 // FIXME: allow quads, cubics to be near coincident?
2356 if (mOther->fVerb == SkPath::kLine_Verb) {
2357 moCount = mOther->fTs.count();
2358 if (moCount >= 3) {
2359 break;
2360 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002361 }
2362 if (++matchIndex >= count) {
2363 return;
2364 }
2365 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002366 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002367 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002368 // look for a pair of nearby T values that map to the same (x,y) value
2369 // if found, see if the pair of other segments share a common point. If
2370 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002371 for (int index = matchIndex + 1; index < count; ++index) {
2372 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002373 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002374 continue;
2375 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002376 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002377 if (tOther->fVerb != SkPath::kLine_Verb) {
2378 continue; // FIXME: allow quads, cubics to be near coincident?
2379 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002380 int toCount = tOther->fTs.count();
2381 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002382 continue;
2383 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002384 const SkPoint* testPt = &xyAtT(test);
2385 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002386 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002387 moCount = toCount;
2388 match = test;
2389 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002390 matchPt = testPt;
2391 continue;
2392 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002393 int moStart = -1;
2394 int moEnd = -1;
2395 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002396 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002397 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002398 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002399 continue;
2400 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002401 if (moSpan.fOther == this) {
2402 if (moSpan.fOtherT == match->fT) {
2403 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002404 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002405 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002406 continue;
2407 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002408 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002409 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2410 moStart = -1;
2411 break;
2412 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002413 SkASSERT(moEnd == -1);
2414 moEnd = moIndex;
2415 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002416 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002417 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002418 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002419 continue;
2420 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002421 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002422 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002423 continue;
2424 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002425 int toStart = -1;
2426 int toEnd = -1;
2427 double toStartT, toEndT;
2428 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2429 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002430 if (toSpan.fDone) {
2431 continue;
2432 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002433 if (toSpan.fOther == this) {
2434 if (toSpan.fOtherT == test->fT) {
2435 toStart = toIndex;
2436 toStartT = toSpan.fT;
2437 }
2438 continue;
2439 }
2440 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002441 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2442 moStart = -1;
2443 break;
2444 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002445 SkASSERT(toEnd == -1);
2446 toEnd = toIndex;
2447 toEndT = toSpan.fT;
2448 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002449 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002450 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2451 if (toStart <= 0 || toEnd <= 0) {
2452 continue;
2453 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002454 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002455 continue;
2456 }
2457 // test to see if the segment between there and here is linear
2458 if (!mOther->isLinear(moStart, moEnd)
2459 || !tOther->isLinear(toStart, toEnd)) {
2460 continue;
2461 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002462 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002463 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002464 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002465 } else {
caryclark@google.com4eeda372012-12-06 21:47:48 +00002466 mOther->addTCoincident(moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002467 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002468 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002469 }
2470
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002471 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002472 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002473 // a) mark spans with either end unsortable as done, or
2474 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2475 // when encountering an unsortable span
2476
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002477 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2478 // and use more concise logic like the old edge walker code?
2479 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002480 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002481 // iterate through T intersections and return topmost
2482 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002483 SkASSERT(!done());
2484 int firstT;
2485 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002486 SkPoint topPt;
2487 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002488 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002489 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002490 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002491 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002492 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002493 const Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00002494 if (span.fUnsortableStart | lastUnsortable) {
2495 goto next;
2496 }
2497 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002498 const SkPoint& intercept = xyAtT(&span);
2499 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2500 && topPt.fX > intercept.fX)) {
2501 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002502 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002503 } else if (topPt == intercept) {
2504 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002505 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002506 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002507 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002508 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002509 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002510 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002511 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002512 int step = 1;
2513 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002514 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002515 step = -1;
2516 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002517 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002518 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002519 // if the topmost T is not on end, or is three-way or more, find left
2520 // look for left-ness from tLeft to firstT (matching y of other)
2521 SkTDArray<Angle> angles;
2522 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002523 addTwoAngles(end, firstT, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002524 buildAngles(firstT, angles, true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002525 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002526 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002527 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002528 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002529 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00002530 if (!sortable) {
2531 return NULL;
2532 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002533 // skip edges that have already been processed
2534 firstT = -1;
2535 Segment* leftSegment;
2536 do {
2537 const Angle* angle = sorted[++firstT];
caryclark@google.comf839c032012-10-26 21:03:50 +00002538 SkASSERT(!angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002539 leftSegment = angle->segment();
2540 tIndex = angle->end();
2541 endIndex = angle->start();
2542 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002543 return leftSegment;
2544 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002545
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002546 // FIXME: not crazy about this
2547 // when the intersections are performed, the other index is into an
2548 // incomplete array. as the array grows, the indices become incorrect
2549 // while the following fixes the indices up again, it isn't smart about
2550 // skipping segments whose indices are already correct
2551 // assuming we leave the code that wrote the index in the first place
2552 void fixOtherTIndex() {
2553 int iCount = fTs.count();
2554 for (int i = 0; i < iCount; ++i) {
2555 Span& iSpan = fTs[i];
2556 double oT = iSpan.fOtherT;
2557 Segment* other = iSpan.fOther;
2558 int oCount = other->fTs.count();
2559 for (int o = 0; o < oCount; ++o) {
2560 Span& oSpan = other->fTs[o];
2561 if (oT == oSpan.fT && this == oSpan.fOther) {
2562 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002563 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002564 }
2565 }
2566 }
2567 }
skia.committer@gmail.comd21444a2012-12-07 02:01:25 +00002568
caryclark@google.com4eeda372012-12-06 21:47:48 +00002569 void init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002570 fDoneSpans = 0;
2571 fOperand = operand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00002572 fXor = evenOdd;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002573 fPts = pts;
2574 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002575 }
2576
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002577 void initWinding(int start, int end, int winding, int oppWinding) {
2578 int local = spanSign(start, end);
2579 if (local * winding >= 0) {
2580 winding += local;
2581 }
2582 local = oppSign(start, end);
2583 if (local * oppWinding >= 0) {
2584 oppWinding += local;
2585 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002586 (void) markAndChaseWinding(start, end, winding, oppWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002587 }
2588
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002589 bool intersected() const {
2590 return fTs.count() > 0;
2591 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002592
2593 bool isConnected(int startIndex, int endIndex) const {
2594 return fTs[startIndex].fWindSum != SK_MinS32
2595 || fTs[endIndex].fWindSum != SK_MinS32;
2596 }
2597
caryclark@google.com235f56a2012-09-14 14:19:30 +00002598 bool isHorizontal() const {
2599 return fBounds.fTop == fBounds.fBottom;
2600 }
2601
caryclark@google.com15fa1382012-05-07 20:49:36 +00002602 bool isLinear(int start, int end) const {
2603 if (fVerb == SkPath::kLine_Verb) {
2604 return true;
2605 }
2606 if (fVerb == SkPath::kQuad_Verb) {
2607 SkPoint qPart[3];
2608 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2609 return QuadIsLinear(qPart);
2610 } else {
2611 SkASSERT(fVerb == SkPath::kCubic_Verb);
2612 SkPoint cPart[4];
2613 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2614 return CubicIsLinear(cPart);
2615 }
2616 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002617
2618 // OPTIMIZE: successive calls could start were the last leaves off
2619 // or calls could specialize to walk forwards or backwards
2620 bool isMissing(double startT) const {
2621 size_t tCount = fTs.count();
2622 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002623 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002624 return false;
2625 }
2626 }
2627 return true;
2628 }
2629
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002630 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002631 int count = fTs.count();
2632 if (count == 2) {
2633 return true;
2634 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002635 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002636 if (approximately_less_than_zero(t)) {
2637 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002638 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002639 if (approximately_greater_than_one(t)) {
2640 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002641 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002642 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002643 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002644
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002645 bool isVertical() const {
2646 return fBounds.fLeft == fBounds.fRight;
2647 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002648
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002649 SkScalar leftMost(int start, int end) const {
2650 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2651 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002652
caryclark@google.com495f8e42012-05-31 13:13:11 +00002653 // this span is excluded by the winding rule -- chase the ends
2654 // as long as they are unambiguous to mark connections as done
2655 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002656 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002657 int index = angle->start();
2658 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002659 return markAndChaseDone(index, endIndex, winding);
2660 }
2661
caryclark@google.com31143cf2012-11-09 22:14:19 +00002662 Span* markAndChaseDone(int index, int endIndex, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002663 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002664 int min = SkMin32(index, endIndex);
2665 markDone(min, winding);
2666 Span* last;
2667 Segment* other = this;
2668 while ((other = other->nextChase(index, step, min, last))) {
2669 other->markDone(min, winding);
2670 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002671 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002672 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002673
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002674 Span* markAndChaseDoneBinary(const Angle* angle, int winding, int oppWinding) {
2675 int index = angle->start();
2676 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002677 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002678 int min = SkMin32(index, endIndex);
2679 markDoneBinary(min, winding, oppWinding);
2680 Span* last;
2681 Segment* other = this;
2682 while ((other = other->nextChase(index, step, min, last))) {
2683 other->markDoneBinary(min, winding, oppWinding);
2684 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002685 return last;
2686 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002687
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002688 Span* markAndChaseDoneBinary(int index, int endIndex) {
2689 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002690 int min = SkMin32(index, endIndex);
2691 markDoneBinary(min);
2692 Span* last;
2693 Segment* other = this;
2694 while ((other = other->nextChase(index, step, min, last))) {
2695 other->markDoneBinary(min);
2696 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002697 return last;
2698 }
2699
caryclark@google.com4eeda372012-12-06 21:47:48 +00002700 Span* markAndChaseWinding(const Angle* angle, const int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002701 int index = angle->start();
2702 int endIndex = angle->end();
skia.committer@gmail.comd21444a2012-12-07 02:01:25 +00002703 int step = SkSign32(endIndex - index);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002704 int min = SkMin32(index, endIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002705 markWinding(min, winding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002706 Span* last;
2707 Segment* other = this;
2708 while ((other = other->nextChase(index, step, min, last))) {
2709 if (other->fTs[min].fWindSum != SK_MinS32) {
2710 SkASSERT(other->fTs[min].fWindSum == winding);
2711 return NULL;
2712 }
2713 other->markWinding(min, winding);
2714 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002715 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002716 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002717
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002718 Span* markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002719 int min = SkMin32(index, endIndex);
2720 int step = SkSign32(endIndex - index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002721 markWinding(min, winding, oppWinding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002722 Span* last;
2723 Segment* other = this;
2724 while ((other = other->nextChase(index, step, min, last))) {
2725 if (other->fTs[min].fWindSum != SK_MinS32) {
2726 SkASSERT(other->fTs[min].fWindSum == winding);
2727 return NULL;
2728 }
2729 other->markWinding(min, winding, oppWinding);
2730 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002731 return last;
2732 }
2733
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002734 Span* markAndChaseWinding(const Angle* angle, int winding, int oppWinding) {
2735 int start = angle->start();
2736 int end = angle->end();
2737 return markAndChaseWinding(start, end, winding, oppWinding);
2738 }
2739
2740 Span* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
2741 bool activeAngle, const Angle* angle) {
2742 SkASSERT(angle->segment() == this);
2743 if (useInnerWinding(maxWinding, sumWinding)) {
2744 maxWinding = sumWinding;
2745 }
2746 if (oppMaxWinding != oppSumWinding && useInnerWinding(oppMaxWinding, oppSumWinding)) {
2747 oppMaxWinding = oppSumWinding;
2748 }
2749 Span* last;
2750 if (activeAngle) {
2751 last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
2752 } else {
2753 last = markAndChaseDoneBinary(angle, maxWinding, oppMaxWinding);
2754 }
2755 return last;
2756 }
2757
caryclark@google.com495f8e42012-05-31 13:13:11 +00002758 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002759 // This may be called when the segment is already marked done. While this
2760 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002761 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002762 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002763 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002764 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002765 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002766 double referenceT = fTs[index].fT;
2767 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002768 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2769 markOneDone(__FUNCTION__, lesser, winding);
2770 }
2771 do {
2772 markOneDone(__FUNCTION__, index, winding);
2773 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002774 }
2775
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002776 void markDoneBinary(int index, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002777 // SkASSERT(!done());
2778 SkASSERT(winding);
2779 double referenceT = fTs[index].fT;
2780 int lesser = index;
2781 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002782 markOneDoneBinary(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002783 }
2784 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002785 markOneDoneBinary(__FUNCTION__, index, winding, oppWinding);
2786 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2787 }
2788
2789 void markDoneBinary(int index) {
2790 double referenceT = fTs[index].fT;
2791 int lesser = index;
2792 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2793 markOneDoneBinary(__FUNCTION__, lesser);
2794 }
2795 do {
2796 markOneDoneBinary(__FUNCTION__, index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002797 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002798 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002799
caryclark@google.com24bec792012-08-20 12:43:57 +00002800 void markOneDone(const char* funName, int tIndex, int winding) {
2801 Span* span = markOneWinding(funName, tIndex, winding);
2802 if (!span) {
2803 return;
2804 }
2805 span->fDone = true;
2806 fDoneSpans++;
2807 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002808
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002809 void markOneDoneBinary(const char* funName, int tIndex) {
2810 Span* span = verifyOneWinding(funName, tIndex);
2811 if (!span) {
2812 return;
2813 }
2814 span->fDone = true;
2815 fDoneSpans++;
2816 }
2817
2818 void markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002819 Span* span = markOneWinding(funName, tIndex, winding, oppWinding);
2820 if (!span) {
2821 return;
2822 }
2823 span->fDone = true;
2824 fDoneSpans++;
2825 }
2826
caryclark@google.com24bec792012-08-20 12:43:57 +00002827 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2828 Span& span = fTs[tIndex];
2829 if (span.fDone) {
2830 return NULL;
2831 }
2832 #if DEBUG_MARK_DONE
2833 debugShowNewWinding(funName, span, winding);
2834 #endif
2835 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002836 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002837 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002838 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002839 span.fWindSum = winding;
2840 return &span;
2841 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002842
caryclark@google.com31143cf2012-11-09 22:14:19 +00002843 Span* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding) {
2844 Span& span = fTs[tIndex];
2845 if (span.fDone) {
2846 return NULL;
2847 }
2848 #if DEBUG_MARK_DONE
2849 debugShowNewWinding(funName, span, winding, oppWinding);
2850 #endif
2851 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
2852 #ifdef SK_DEBUG
2853 SkASSERT(abs(winding) <= gDebugMaxWindSum);
2854 #endif
2855 span.fWindSum = winding;
2856 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
2857 #ifdef SK_DEBUG
2858 SkASSERT(abs(oppWinding) <= gDebugMaxWindSum);
2859 #endif
2860 span.fOppSum = oppWinding;
2861 return &span;
2862 }
2863
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002864 Span* verifyOneWinding(const char* funName, int tIndex) {
2865 Span& span = fTs[tIndex];
2866 if (span.fDone) {
2867 return NULL;
2868 }
2869 #if DEBUG_MARK_DONE
2870 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
2871 #endif
2872 SkASSERT(span.fWindSum != SK_MinS32);
2873 SkASSERT(span.fOppSum != SK_MinS32);
2874 return &span;
2875 }
2876
caryclark@google.comf839c032012-10-26 21:03:50 +00002877 // note that just because a span has one end that is unsortable, that's
2878 // not enough to mark it done. The other end may be sortable, allowing the
2879 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002880 void markUnsortable(int start, int end) {
2881 Span* span = &fTs[start];
2882 if (start < end) {
2883 span->fUnsortableStart = true;
2884 } else {
2885 --span;
2886 span->fUnsortableEnd = true;
2887 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002888 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002889 return;
2890 }
2891 span->fDone = true;
2892 fDoneSpans++;
2893 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002894
caryclark@google.com59823f72012-08-09 18:17:47 +00002895 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002896 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002897 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002898 double referenceT = fTs[index].fT;
2899 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002900 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2901 markOneWinding(__FUNCTION__, lesser, winding);
2902 }
2903 do {
2904 markOneWinding(__FUNCTION__, index, winding);
2905 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002906 }
2907
2908 void markWinding(int index, int winding, int oppWinding) {
2909 // SkASSERT(!done());
caryclark@google.com4eeda372012-12-06 21:47:48 +00002910 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002911 double referenceT = fTs[index].fT;
2912 int lesser = index;
2913 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2914 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002915 }
2916 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002917 markOneWinding(__FUNCTION__, index, winding, oppWinding);
2918 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002919 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002920
caryclark@google.com2ddff932012-08-07 21:25:27 +00002921 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002922 int nextDoorWind = SK_MaxS32;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002923 int nextOppWind = SK_MaxS32;
caryclark@google.com0c803d02012-08-06 11:15:47 +00002924 if (tIndex > 0) {
2925 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002926 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002927 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002928 nextOppWind = below.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00002929 }
2930 }
2931 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2932 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002933 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002934 nextDoorWind = above.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002935 nextOppWind = above.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00002936 }
2937 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002938 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2939 const Span& below = fTs[tIndex - 1];
2940 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002941 nextOppWind = below.fOppValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002942 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002943 if (nextDoorWind != SK_MaxS32) {
2944 Span& newSpan = fTs[tIndex];
2945 newSpan.fWindValue = nextDoorWind;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002946 newSpan.fOppValue = nextOppWind;
caryclark@google.com4eeda372012-12-06 21:47:48 +00002947 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002948 newSpan.fDone = true;
2949 ++fDoneSpans;
2950 }
2951 }
2952 }
2953
caryclark@google.com9764cc62012-07-12 19:29:45 +00002954 // return span if when chasing, two or more radiating spans are not done
2955 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2956 // candidate and the remaining spans have windValue == 0 (canceled by
2957 // coincidence). The coincident edges could either be removed altogether,
2958 // or this code could be more complicated in detecting this case. Worth it?
2959 bool multipleSpans(int end) const {
2960 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002961 }
2962
caryclark@google.com4eeda372012-12-06 21:47:48 +00002963 Segment* nextChase(int& index, const int step, int& min, Span*& last) const {
2964 int end = nextExactSpan(index, step);
2965 SkASSERT(end >= 0);
2966 if (multipleSpans(end)) {
2967 last = &fTs[end];
2968 return NULL;
2969 }
2970 const Span& endSpan = fTs[end];
2971 Segment* other = endSpan.fOther;
2972 index = endSpan.fOtherIndex;
2973 int otherEnd = other->nextExactSpan(index, step);
2974 min = SkMin32(index, otherEnd);
2975 return other;
2976 }
2977
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002978 // This has callers for two different situations: one establishes the end
2979 // of the current span, and one establishes the beginning of the next span
2980 // (thus the name). When this is looking for the end of the current span,
2981 // coincidence is found when the beginning Ts contain -step and the end
2982 // contains step. When it is looking for the beginning of the next, the
2983 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002984 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002985 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002986 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002987 int count = fTs.count();
2988 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002989 while (step > 0 ? ++to < count : --to >= 0) {
2990 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002991 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002992 continue;
2993 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002994 return to;
2995 }
2996 return -1;
2997 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00002998
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002999 // FIXME
3000 // this returns at any difference in T, vs. a preset minimum. It may be
3001 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003002 // OPTIMIZATION splitting this into separate loops for up/down steps
3003 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003004 int nextExactSpan(int from, int step) const {
3005 const Span& fromSpan = fTs[from];
3006 int count = fTs.count();
3007 int to = from;
3008 while (step > 0 ? ++to < count : --to >= 0) {
3009 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00003010 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003011 continue;
3012 }
3013 return to;
3014 }
3015 return -1;
3016 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003017
caryclark@google.com235f56a2012-09-14 14:19:30 +00003018 bool operand() const {
3019 return fOperand;
3020 }
3021
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003022 int oppSign(const Angle* angle) const {
3023 SkASSERT(angle->segment() == this);
3024 return oppSign(angle->start(), angle->end());
3025 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003026
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003027 int oppSign(int startIndex, int endIndex) const {
3028 int result = startIndex < endIndex ? -fTs[startIndex].fOppValue
3029 : fTs[endIndex].fOppValue;
3030#if DEBUG_WIND_BUMP
3031 SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
3032#endif
3033 return result;
3034 }
3035
caryclark@google.com31143cf2012-11-09 22:14:19 +00003036 int oppSum(int tIndex) const {
3037 return fTs[tIndex].fOppSum;
3038 }
3039
3040 int oppSum(const Angle* angle) const {
3041 int lesser = SkMin32(angle->start(), angle->end());
3042 return fTs[lesser].fOppSum;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003043 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003044
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003045 int oppValue(int tIndex) const {
3046 return fTs[tIndex].fOppValue;
3047 }
3048
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003049 const SkPoint* pts() const {
3050 return fPts;
3051 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003052
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003053 void reset() {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003054 init(NULL, (SkPath::Verb) -1, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003055 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
3056 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003057 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00003058
caryclark@google.com4eeda372012-12-06 21:47:48 +00003059 void setOppXor(bool isOppXor) {
3060 fOppXor = isOppXor;
3061 }
3062
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003063 void setUpWindings(int index, int endIndex, int& sumMiWinding, int& sumSuWinding,
3064 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
3065 int deltaSum = spanSign(index, endIndex);
3066 int oppDeltaSum = oppSign(index, endIndex);
3067 if (operand()) {
3068 maxWinding = sumSuWinding;
3069 sumWinding = sumSuWinding -= deltaSum;
3070 oppMaxWinding = sumMiWinding;
3071 oppSumWinding = sumMiWinding -= oppDeltaSum;
3072 } else {
3073 maxWinding = sumMiWinding;
3074 sumWinding = sumMiWinding -= deltaSum;
3075 oppMaxWinding = sumSuWinding;
3076 oppSumWinding = sumSuWinding -= oppDeltaSum;
3077 }
3078 }
3079
caryclark@google.comf839c032012-10-26 21:03:50 +00003080 // This marks all spans unsortable so that this info is available for early
3081 // exclusion in find top and others. This could be optimized to only mark
3082 // adjacent spans that unsortable. However, this makes it difficult to later
3083 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003084 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003085 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003086 int angleCount = angles.count();
3087 int angleIndex;
3088 angleList.setReserve(angleCount);
3089 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003090 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00003091 *angleList.append() = &angle;
3092 sortable &= !angle.unsortable();
3093 }
3094 if (sortable) {
3095 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3096 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3097 if (angles[angleIndex].unsortable()) {
3098 sortable = false;
3099 break;
3100 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003101 }
3102 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003103 if (!sortable) {
3104 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3105 Angle& angle = angles[angleIndex];
3106 angle.segment()->markUnsortable(angle.start(), angle.end());
3107 }
3108 }
3109 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003110 }
3111
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003112 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003113 const Span& span(int tIndex) const {
3114 return fTs[tIndex];
3115 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003116
caryclark@google.com235f56a2012-09-14 14:19:30 +00003117 int spanSign(const Angle* angle) const {
3118 SkASSERT(angle->segment() == this);
3119 return spanSign(angle->start(), angle->end());
3120 }
3121
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003122 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003123 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue
3124 : fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003125#if DEBUG_WIND_BUMP
3126 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3127#endif
3128 return result;
3129 }
3130
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003131 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003132 double t(int tIndex) const {
3133 return fTs[tIndex].fT;
3134 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003135
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003136 bool tiny(const Angle* angle) const {
3137 int start = angle->start();
3138 int end = angle->end();
caryclark@google.comf839c032012-10-26 21:03:50 +00003139 const Span& mSpan = fTs[SkMin32(start, end)];
3140 return mSpan.fTiny;
3141 }
3142
caryclark@google.com18063442012-07-25 12:05:18 +00003143 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3144 double start) {
3145 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003146 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003147 *outsideTs.append() = end;
3148 *outsideTs.append() = start;
3149 }
3150 }
skia.committer@gmail.comd21444a2012-12-07 02:01:25 +00003151
caryclark@google.com24bec792012-08-20 12:43:57 +00003152 void undoneSpan(int& start, int& end) {
3153 size_t tCount = fTs.count();
3154 size_t index;
3155 for (index = 0; index < tCount; ++index) {
3156 if (!fTs[index].fDone) {
3157 break;
3158 }
3159 }
3160 SkASSERT(index < tCount - 1);
3161 start = index;
3162 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003163 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003164 SkASSERT(index < tCount);
3165 SkASSERT(index < tCount);
3166 end = index;
3167 }
caryclark@google.com18063442012-07-25 12:05:18 +00003168
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003169 bool unsortable(int index) const {
3170 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3171 }
3172
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003173 void updatePts(const SkPoint pts[]) {
3174 fPts = pts;
3175 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003176
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003177 int updateOppWinding(int index, int endIndex) const {
3178 int lesser = SkMin32(index, endIndex);
3179 int oppWinding = oppSum(lesser);
3180 int oppSpanWinding = oppSign(index, endIndex);
3181 if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)) {
3182 oppWinding -= oppSpanWinding;
3183 }
3184 return oppWinding;
3185 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00003186
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003187 int updateOppWinding(const Angle* angle) const {
3188 int startIndex = angle->start();
3189 int endIndex = angle->end();
3190 return updateOppWinding(endIndex, startIndex);
3191 }
3192
3193 int updateOppWindingReverse(const Angle* angle) const {
3194 int startIndex = angle->start();
3195 int endIndex = angle->end();
3196 return updateOppWinding(startIndex, endIndex);
3197 }
3198
3199 int updateWinding(int index, int endIndex) const {
3200 int lesser = SkMin32(index, endIndex);
3201 int winding = windSum(lesser);
3202 int spanWinding = spanSign(index, endIndex);
3203 if (useInnerWinding(winding - spanWinding, winding)) {
3204 winding -= spanWinding;
3205 }
3206 return winding;
3207 }
3208
3209 int updateWinding(const Angle* angle) const {
3210 int startIndex = angle->start();
3211 int endIndex = angle->end();
3212 return updateWinding(endIndex, startIndex);
3213 }
3214
3215 int updateWindingReverse(const Angle* angle) const {
3216 int startIndex = angle->start();
3217 int endIndex = angle->end();
3218 return updateWinding(startIndex, endIndex);
3219 }
3220
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003221 SkPath::Verb verb() const {
3222 return fVerb;
3223 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003224
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003225 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003226 return fTs[tIndex].fWindSum;
3227 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003228
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003229 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003230 int start = angle->start();
3231 int end = angle->end();
3232 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003233 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003234 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003235
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003236 int windValue(int tIndex) const {
3237 return fTs[tIndex].fWindValue;
3238 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003239
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003240 int windValue(const Angle* angle) const {
3241 int start = angle->start();
3242 int end = angle->end();
3243 int index = SkMin32(start, end);
3244 return windValue(index);
3245 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003246
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003247 SkScalar xAtT(const Span* span) const {
3248 return xyAtT(span).fX;
3249 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003250
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003251 const SkPoint& xyAtT(int index) const {
3252 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003253 }
3254
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003255 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003256 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003257 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003258 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003259 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003260 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003261 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003262 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003263 }
3264 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003265 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003266 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003267
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003268 SkScalar yAtT(int index) const {
3269 return yAtT(&fTs[index]);
3270 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003271
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003272 SkScalar yAtT(const Span* span) const {
3273 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003274 }
3275
caryclark@google.com4eeda372012-12-06 21:47:48 +00003276 void zeroCoincidentOpp(Span* oTest, int index) {
3277 Span* const test = &fTs[index];
3278 Span* end = test;
3279 do {
3280 end->fOppValue = 0;
3281 end = &fTs[++index];
3282 } while (approximately_negative(end->fT - test->fT));
3283 }
3284
3285 void zeroCoincidentOther(Span* test, const double tRatio, const double oEndT, int oIndex) {
3286 Span* const oTest = &fTs[oIndex];
3287 Span* oEnd = oTest;
3288 const double startT = test->fT;
3289 const double oStartT = oTest->fT;
3290 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
3291 while (!approximately_negative(oEndT - oEnd->fT)
3292 && approximately_negative(oEnd->fT - otherTMatch)) {
3293 oEnd->fOppValue = 0;
3294 oEnd = &fTs[++oIndex];
3295 }
3296 }
3297
3298 void zeroSpan(Span* span) {
3299 SkASSERT(span->fWindValue > 0 || span->fOppValue > 0);
caryclark@google.com6ec15262012-11-16 20:16:50 +00003300 span->fWindValue = 0;
caryclark@google.com729e1c42012-11-21 21:36:34 +00003301 span->fOppValue = 0;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003302 SkASSERT(!span->fDone);
3303 span->fDone = true;
3304 ++fDoneSpans;
caryclark@google.com6ec15262012-11-16 20:16:50 +00003305 }
3306
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003307#if DEBUG_DUMP
3308 void dump() const {
3309 const char className[] = "Segment";
3310 const int tab = 4;
3311 for (int i = 0; i < fTs.count(); ++i) {
3312 SkPoint out;
3313 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3314 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003315 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003316 tab + sizeof(className), className, fID,
3317 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003318 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003319 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003320 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003321 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003322 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003323 }
3324#endif
3325
caryclark@google.com47580692012-07-23 12:14:49 +00003326#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003327 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003328 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003329 for (int i = 0; i < fTs.count(); ++i) {
3330 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3331 return;
3332 }
3333 }
3334 SkASSERT(0);
3335 }
3336#endif
3337
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003338#if DEBUG_DUMP
3339 int debugID() const {
3340 return fID;
3341 }
3342#endif
3343
caryclark@google.com24bec792012-08-20 12:43:57 +00003344#if DEBUG_WINDING
3345 void debugShowSums() const {
3346 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3347 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3348 for (int i = 0; i < fTs.count(); ++i) {
3349 const Span& span = fTs[i];
3350 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3351 if (span.fWindSum == SK_MinS32) {
3352 SkDebugf("?");
3353 } else {
3354 SkDebugf("%d", span.fWindSum);
3355 }
3356 SkDebugf("]");
3357 }
3358 SkDebugf("\n");
3359 }
3360#endif
3361
caryclark@google.comcc905052012-07-25 20:59:42 +00003362#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003363 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003364 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003365 int lastWind = -1;
3366 int lastOpp = -1;
3367 double lastT = -1;
3368 int i;
3369 for (i = 0; i < fTs.count(); ++i) {
3370 bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
3371 || lastOpp != fTs[i].fOppValue;
3372 if (change && lastWind >= 0) {
3373 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3374 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3375 }
3376 if (change) {
3377 SkDebugf(" [o=%d", fTs[i].fOther->fID);
3378 lastWind = fTs[i].fWindValue;
3379 lastOpp = fTs[i].fOppValue;
3380 lastT = fTs[i].fT;
3381 } else {
3382 SkDebugf(",%d", fTs[i].fOther->fID);
3383 }
3384 }
3385 if (i <= 0) {
3386 return;
3387 }
3388 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3389 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3390 if (fOperand) {
3391 SkDebugf(" operand");
3392 }
3393 if (done()) {
3394 SkDebugf(" done");
caryclark@google.com47580692012-07-23 12:14:49 +00003395 }
3396 SkDebugf("\n");
3397 }
3398#endif
3399
caryclark@google.com027de222012-07-12 12:52:50 +00003400#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003401 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003402 if (done()) {
3403 return;
3404 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003405#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3406 int lastId = -1;
3407 double lastT = -1;
3408#endif
caryclark@google.com027de222012-07-12 12:52:50 +00003409 for (int i = 0; i < fTs.count(); ++i) {
3410 if (fTs[i].fDone) {
3411 continue;
3412 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003413#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3414 if (lastId == fID && lastT == fTs[i].fT) {
3415 continue;
3416 }
3417 lastId = fID;
3418 lastT = fTs[i].fT;
3419#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003420 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003421 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3422 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3423 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3424 }
3425 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003426 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003427 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003428 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003429 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3430 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3431 if (fTs[i].fWindSum == SK_MinS32) {
3432 SkDebugf("?");
3433 } else {
3434 SkDebugf("%d", fTs[i].fWindSum);
3435 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003436 SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003437 }
3438 }
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +00003439
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003440 // This isn't useful yet -- but leaving it in for now in case i think of something
3441 // to use it for
3442 void validateActiveSpans() const {
3443 if (done()) {
3444 return;
3445 }
3446 int tCount = fTs.count();
3447 for (int index = 0; index < tCount; ++index) {
3448 if (fTs[index].fDone) {
3449 continue;
3450 }
3451 // count number of connections which are not done
3452 int first = index;
3453 double baseT = fTs[index].fT;
3454 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3455 --first;
3456 }
3457 int last = index;
3458 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3459 ++last;
3460 }
3461 int connections = 0;
3462 connections += first > 0 && !fTs[first - 1].fDone;
3463 for (int test = first; test <= last; ++test) {
3464 connections += !fTs[test].fDone;
3465 const Segment* other = fTs[test].fOther;
3466 int oIndex = fTs[test].fOtherIndex;
3467 connections += !other->fTs[oIndex].fDone;
3468 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3469 }
3470 // SkASSERT(!(connections & 1));
3471 }
3472 }
caryclark@google.com027de222012-07-12 12:52:50 +00003473#endif
3474
caryclark@google.com0c803d02012-08-06 11:15:47 +00003475#if DEBUG_MARK_DONE
3476 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3477 const SkPoint& pt = xyAtT(&span);
3478 SkDebugf("%s id=%d", fun, fID);
3479 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3480 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3481 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3482 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003483 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3484 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3485 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3486 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003487 if (span.fWindSum == SK_MinS32) {
3488 SkDebugf("?");
3489 } else {
3490 SkDebugf("%d", span.fWindSum);
3491 }
3492 SkDebugf(" windValue=%d\n", span.fWindValue);
3493 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003494
3495 void debugShowNewWinding(const char* fun, const Span& span, int winding, int oppWinding) {
3496 const SkPoint& pt = xyAtT(&span);
3497 SkDebugf("%s id=%d", fun, fID);
3498 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3499 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3500 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3501 }
3502 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3503 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3504 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d newOppSum=%d oppSum=",
3505 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
3506 winding, oppWinding);
3507 if (span.fOppSum == SK_MinS32) {
3508 SkDebugf("?");
3509 } else {
3510 SkDebugf("%d", span.fOppSum);
3511 }
3512 SkDebugf(" windSum=");
3513 if (span.fWindSum == SK_MinS32) {
3514 SkDebugf("?");
3515 } else {
3516 SkDebugf("%d", span.fWindSum);
3517 }
3518 SkDebugf(" windValue=%d\n", span.fWindValue);
3519 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003520#endif
3521
caryclark@google.com47580692012-07-23 12:14:49 +00003522#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003523 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com31143cf2012-11-09 22:14:19 +00003524 const int contourWinding, const int oppContourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003525 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003526 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003527 int lastSum = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003528 int oppLastSum = oppContourWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003529 const Angle* firstAngle = angles[first];
3530 int windSum = lastSum - spanSign(firstAngle);
3531 int oppoSign = oppSign(firstAngle);
3532 int oppWindSum = oppLastSum - oppoSign;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003533 SkDebugf("%s %s contourWinding=%d oppContourWinding=%d sign=%d\n", fun, __FUNCTION__,
3534 contourWinding, oppContourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003535 int index = first;
3536 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003537 do {
3538 const Angle& angle = *angles[index];
3539 const Segment& segment = *angle.segment();
3540 int start = angle.start();
3541 int end = angle.end();
3542 const Span& sSpan = segment.fTs[start];
3543 const Span& eSpan = segment.fTs[end];
3544 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com31143cf2012-11-09 22:14:19 +00003545 bool opp = segment.fOperand ^ fOperand;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003546 if (!firstTime) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003547 oppoSign = segment.oppSign(&angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003548 if (opp) {
3549 oppLastSum = oppWindSum;
3550 oppWindSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003551 if (oppoSign) {
3552 lastSum = windSum;
3553 windSum -= oppoSign;
3554 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003555 } else {
3556 lastSum = windSum;
3557 windSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003558 if (oppoSign) {
3559 oppLastSum = oppWindSum;
3560 oppWindSum -= oppoSign;
3561 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003562 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00003563 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003564 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 +00003565 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003566 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003567 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003568 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3569 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3570 mSpan.fWindValue);
3571 if (mSpan.fWindSum == SK_MinS32) {
3572 SkDebugf("?");
3573 } else {
3574 SkDebugf("%d", mSpan.fWindSum);
3575 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003576 int last, wind;
3577 if (opp) {
3578 last = oppLastSum;
3579 wind = oppWindSum;
3580 } else {
3581 last = lastSum;
3582 wind = windSum;
3583 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003584 if (!oppoSign) {
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003585 SkDebugf(" %d->%d (max=%d)", last, wind,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003586 useInnerWinding(last, wind) ? wind : last);
3587 } else {
3588 SkDebugf(" %d->%d (%d->%d)", last, wind, opp ? lastSum : oppLastSum,
3589 opp ? windSum : oppWindSum);
3590 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003591 SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003592#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003593 angle.debugShow(segment.xyAtT(&sSpan));
3594#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003595 ++index;
3596 if (index == angles.count()) {
3597 index = 0;
3598 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003599 if (firstTime) {
3600 firstTime = false;
3601 }
caryclark@google.com47580692012-07-23 12:14:49 +00003602 } while (index != first);
3603 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003604
3605 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first) {
3606 const Angle* firstAngle = angles[first];
3607 const Segment* segment = firstAngle->segment();
3608 int winding = segment->updateWinding(firstAngle);
3609 int oppWinding = segment->updateOppWinding(firstAngle);
3610 debugShowSort(fun, angles, first, winding, oppWinding);
3611 }
3612
caryclark@google.com47580692012-07-23 12:14:49 +00003613#endif
3614
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003615#if DEBUG_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003616 static char as_digit(int value) {
3617 return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
3618 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003619#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003620
caryclark@google.com729e1c42012-11-21 21:36:34 +00003621#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003622 int debugShowWindingValues(int slotCount, int ofInterest) const {
3623 if (!(1 << fID & ofInterest)) {
3624 return 0;
3625 }
3626 int sum = 0;
3627 SkTDArray<char> slots;
3628 slots.setCount(slotCount * 2);
3629 memset(slots.begin(), ' ', slotCount * 2);
3630 for (int i = 0; i < fTs.count(); ++i) {
3631 // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
3632 // continue;
3633 // }
3634 sum += fTs[i].fWindValue;
3635 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
3636 sum += fTs[i].fOppValue;
3637 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
3638 }
3639 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
3640 slots.begin() + slotCount);
3641 return sum;
3642 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003643#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003644
caryclark@google.com729e1c42012-11-21 21:36:34 +00003645#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003646 bool debugVerifyWinding(int start, int end, int winding) const {
3647 const Span& span = fTs[SkMin32(start, end)];
3648 int spanWinding = span.fWindSum;
3649 if (spanWinding == SK_MinS32) {
3650 return true;
3651 }
3652 int spanSign = SkSign32(start - end);
3653 int signedVal = spanSign * span.fWindValue;
3654 if (signedVal < 0) {
3655 spanWinding -= signedVal;
3656 }
3657 return span.fWindSum == winding;
3658 }
3659#endif
3660
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003661private:
3662 const SkPoint* fPts;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003663 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003664 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com4eeda372012-12-06 21:47:48 +00003665 // OPTIMIZATION: could pack donespans, verb, operand, xor into 1 int-sized value
caryclark@google.com24bec792012-08-20 12:43:57 +00003666 int fDoneSpans; // quick check that segment is finished
caryclark@google.com4eeda372012-12-06 21:47:48 +00003667 // OPTIMIZATION: force the following to be byte-sized
3668 SkPath::Verb fVerb;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003669 bool fOperand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003670 bool fXor; // set if original contour had even-odd fill
3671 bool fOppXor; // set if opposite operand had even-odd fill
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003672#if DEBUG_DUMP
3673 int fID;
3674#endif
3675};
3676
caryclark@google.comb9738012012-07-03 19:53:30 +00003677class Contour;
3678
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003679struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003680 Contour* fContours[2];
3681 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003682 double fTs[2][2];
3683};
3684
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003685class Contour {
3686public:
3687 Contour() {
3688 reset();
3689#if DEBUG_DUMP
3690 fID = ++gContourID;
3691#endif
3692 }
3693
3694 bool operator<(const Contour& rh) const {
3695 return fBounds.fTop == rh.fBounds.fTop
3696 ? fBounds.fLeft < rh.fBounds.fLeft
3697 : fBounds.fTop < rh.fBounds.fTop;
3698 }
3699
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003700 void addCoincident(int index, Contour* other, int otherIndex,
3701 const Intersections& ts, bool swap) {
3702 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003703 coincidence.fContours[0] = this; // FIXME: no need to store
caryclark@google.comb9738012012-07-03 19:53:30 +00003704 coincidence.fContours[1] = other;
3705 coincidence.fSegments[0] = index;
3706 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003707 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3708 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3709 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3710 coincidence.fTs[swap][0] = ts.fT[0][0];
3711 coincidence.fTs[swap][1] = ts.fT[0][1];
3712 coincidence.fTs[!swap][0] = ts.fT[1][0];
3713 coincidence.fTs[!swap][1] = ts.fT[1][1];
3714 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3715 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3716 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3717 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3718 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3719 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3720 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003721 }
3722
3723 void addCross(const Contour* crosser) {
3724#ifdef DEBUG_CROSS
3725 for (int index = 0; index < fCrosses.count(); ++index) {
3726 SkASSERT(fCrosses[index] != crosser);
3727 }
3728#endif
3729 *fCrosses.append() = crosser;
3730 }
3731
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003732 void addCubic(const SkPoint pts[4]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003733 fSegments.push_back().addCubic(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003734 fContainsCurves = true;
3735 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003736
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003737 int addLine(const SkPoint pts[2]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003738 fSegments.push_back().addLine(pts, fOperand, fXor);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003739 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003740 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003741
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003742 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3743 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3744 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003745
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003746 int addQuad(const SkPoint pts[3]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003747 fSegments.push_back().addQuad(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003748 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003749 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003750 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003751
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003752 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3753 containsIntercepts();
3754 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3755 }
3756
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003757 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003758 return fBounds;
3759 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003760
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003761 void complete() {
3762 setBounds();
3763 fContainsIntercepts = false;
3764 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003765
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003766 void containsIntercepts() {
3767 fContainsIntercepts = true;
3768 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003769
rmistry@google.comd6176b02012-08-23 18:14:13 +00003770 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003771 int &tIndex, double& hitT) {
3772 int segmentCount = fSegments.count();
3773 const Segment* bestSegment = NULL;
3774 for (int test = 0; test < segmentCount; ++test) {
3775 Segment* testSegment = &fSegments[test];
3776 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003777 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003778 continue;
3779 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003780 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003781 continue;
3782 }
3783 if (bounds.fLeft > basePt.fX) {
3784 continue;
3785 }
3786 if (bounds.fRight < basePt.fX) {
3787 continue;
3788 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003789 if (bounds.fLeft == bounds.fRight) {
3790 continue;
3791 }
3792 #if 0
3793 bool leftHalf = bounds.fLeft == basePt.fX;
3794 bool rightHalf = bounds.fRight == basePt.fX;
3795 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3796 basePt, leftHalf, rightHalf)) {
3797 continue;
3798 }
3799 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003800 double testHitT;
3801 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3802 if (testT >= 0) {
3803 bestSegment = testSegment;
3804 tIndex = testT;
3805 hitT = testHitT;
3806 }
3807 }
3808 return bestSegment;
3809 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003810
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003811 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003812 for (int index = 0; index < fCrosses.count(); ++index) {
3813 if (fCrosses[index] == crosser) {
3814 return true;
3815 }
3816 }
3817 return false;
3818 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003819
caryclark@google.comf839c032012-10-26 21:03:50 +00003820 const SkPoint& end() const {
3821 const Segment& segment = fSegments.back();
3822 return segment.pts()[segment.verb()];
3823 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003824
caryclark@google.com4eeda372012-12-06 21:47:48 +00003825 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003826 int segmentCount = fSegments.count();
3827 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003828 fSegments[sIndex].findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003829 }
3830 }
3831
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003832 void fixOtherTIndex() {
3833 int segmentCount = fSegments.count();
3834 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3835 fSegments[sIndex].fixOtherTIndex();
3836 }
3837 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003838
caryclark@google.com31143cf2012-11-09 22:14:19 +00003839 bool operand() const {
3840 return fOperand;
3841 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003842
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003843 void reset() {
3844 fSegments.reset();
3845 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003846 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003847 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003848
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003849 void resolveCoincidence(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003850 int count = fCoincidences.count();
3851 for (int index = 0; index < count; ++index) {
3852 Coincidence& coincidence = fCoincidences[index];
caryclark@google.com4eeda372012-12-06 21:47:48 +00003853 SkASSERT(coincidence.fContours[0] == this);
caryclark@google.comb9738012012-07-03 19:53:30 +00003854 int thisIndex = coincidence.fSegments[0];
caryclark@google.com4eeda372012-12-06 21:47:48 +00003855 Segment& thisOne = fSegments[thisIndex];
3856 if (thisOne.done()) {
3857 continue;
3858 }
3859 Contour* otherContour = coincidence.fContours[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00003860 int otherIndex = coincidence.fSegments[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00003861 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00003862 if (other.done()) {
3863 continue;
3864 }
caryclark@google.com47580692012-07-23 12:14:49 +00003865 #if DEBUG_CONCIDENT
3866 thisOne.debugShowTs();
3867 other.debugShowTs();
3868 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003869 double startT = coincidence.fTs[0][0];
3870 double endT = coincidence.fTs[0][1];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003871 bool cancelers = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003872 if (startT > endT) {
3873 SkTSwap<double>(startT, endT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003874 cancelers ^= true; // FIXME: just assign true
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003875 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003876 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003877 double oStartT = coincidence.fTs[1][0];
3878 double oEndT = coincidence.fTs[1][1];
3879 if (oStartT > oEndT) {
3880 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003881 cancelers ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003882 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003883 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00003884 bool opp = fOperand ^ otherContour->fOperand;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003885 if (cancelers && !opp) {
3886 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00003887 if (startT > 0 || oEndT < 1
3888 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3889 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003890 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003891 if (oStartT > 0 || endT < 1
3892 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3893 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003894 }
3895 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003896 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003897 if (startT > 0 || oStartT > 0
3898 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003899 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003900 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003901 if (endT < 1 || oEndT < 1
3902 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003903 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003904 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003905 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003906 }
caryclark@google.com47580692012-07-23 12:14:49 +00003907 #if DEBUG_CONCIDENT
3908 thisOne.debugShowTs();
3909 other.debugShowTs();
3910 #endif
caryclark@google.com729e1c42012-11-21 21:36:34 +00003911 #if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003912 debugShowWindingValues(contourList);
3913 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003914 }
3915 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003916
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003917 const SkTArray<Segment>& segments() {
3918 return fSegments;
3919 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003920
caryclark@google.com235f56a2012-09-14 14:19:30 +00003921 void setOperand(bool isOp) {
3922 fOperand = isOp;
3923 }
skia.committer@gmail.comd21444a2012-12-07 02:01:25 +00003924
caryclark@google.com4eeda372012-12-06 21:47:48 +00003925 void setOppXor(bool isOppXor) {
3926 fOppXor = isOppXor;
3927 int segmentCount = fSegments.count();
3928 for (int test = 0; test < segmentCount; ++test) {
3929 fSegments[test].setOppXor(isOppXor);
3930 }
3931 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003932
caryclark@google.com235f56a2012-09-14 14:19:30 +00003933 void setXor(bool isXor) {
3934 fXor = isXor;
3935 }
3936
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003937 void sortSegments() {
3938 int segmentCount = fSegments.count();
3939 fSortedSegments.setReserve(segmentCount);
3940 for (int test = 0; test < segmentCount; ++test) {
3941 *fSortedSegments.append() = &fSegments[test];
3942 }
3943 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
3944 fFirstSorted = 0;
3945 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003946
caryclark@google.comf839c032012-10-26 21:03:50 +00003947 const SkPoint& start() const {
3948 return fSegments.front().pts()[0];
3949 }
3950
3951 void toPath(PathWrapper& path) const {
3952 int segmentCount = fSegments.count();
3953 const SkPoint& pt = fSegments.front().pts()[0];
3954 path.deferredMove(pt);
3955 for (int test = 0; test < segmentCount; ++test) {
3956 fSegments[test].addCurveTo(0, 1, path, true);
3957 }
3958 path.close();
3959 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003960
caryclark@google.comf839c032012-10-26 21:03:50 +00003961 void toPartialBackward(PathWrapper& path) const {
3962 int segmentCount = fSegments.count();
3963 for (int test = segmentCount - 1; test >= 0; --test) {
3964 fSegments[test].addCurveTo(1, 0, path, true);
3965 }
3966 }
3967
3968 void toPartialForward(PathWrapper& path) const {
3969 int segmentCount = fSegments.count();
3970 for (int test = 0; test < segmentCount; ++test) {
3971 fSegments[test].addCurveTo(0, 1, path, true);
3972 }
3973 }
3974
3975#if 0 // FIXME: obsolete, remove
caryclark@google.com15fa1382012-05-07 20:49:36 +00003976 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3977 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003978 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003979 // segments' top, and not a true sort, so it could be ameniable to regular
3980 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003981 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003982 int segmentCount = fSegments.count();
3983 SkASSERT(segmentCount > 0);
3984 int best = -1;
3985 Segment* bestSegment = NULL;
3986 while (++best < segmentCount) {
3987 Segment* testSegment = &fSegments[best];
3988 if (testSegment->done()) {
3989 continue;
3990 }
3991 bestSegment = testSegment;
3992 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003993 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003994 if (!bestSegment) {
3995 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003996 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003997 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003998 for (int test = best + 1; test < segmentCount; ++test) {
3999 Segment* testSegment = &fSegments[test];
4000 if (testSegment->done()) {
4001 continue;
4002 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004003 if (testSegment->bounds().fTop > bestTop) {
4004 continue;
4005 }
4006 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00004007 if (bestTop > testTop) {
4008 bestTop = testTop;
4009 bestSegment = testSegment;
4010 }
4011 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004012 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004013 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004014 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004015#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004016
caryclark@google.comf839c032012-10-26 21:03:50 +00004017 Segment* topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004018 int segmentCount = fSortedSegments.count();
4019 SkASSERT(segmentCount > 0);
4020 Segment* bestSegment = NULL;
caryclark@google.comf839c032012-10-26 21:03:50 +00004021 int sortedIndex = fFirstSorted;
4022 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
4023 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004024 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00004025 if (sortedIndex == fFirstSorted) {
4026 ++fFirstSorted;
4027 }
4028 continue;
4029 }
4030 SkPoint testXY;
4031 testSegment->activeLeftTop(testXY);
4032 if (testXY.fY < topLeft.fY) {
4033 continue;
4034 }
4035 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
4036 continue;
4037 }
4038 if (bestXY.fY < testXY.fY) {
4039 continue;
4040 }
4041 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004042 continue;
4043 }
4044 bestSegment = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00004045 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004046 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004047 return bestSegment;
4048 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004049
caryclark@google.com24bec792012-08-20 12:43:57 +00004050 Segment* undoneSegment(int& start, int& end) {
4051 int segmentCount = fSegments.count();
4052 for (int test = 0; test < segmentCount; ++test) {
4053 Segment* testSegment = &fSegments[test];
4054 if (testSegment->done()) {
4055 continue;
4056 }
4057 testSegment->undoneSpan(start, end);
4058 return testSegment;
4059 }
4060 return NULL;
4061 }
4062
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004063 int updateSegment(int index, const SkPoint* pts) {
4064 Segment& segment = fSegments[index];
4065 segment.updatePts(pts);
4066 return segment.verb() + 1;
4067 }
4068
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004069#if DEBUG_TEST
4070 SkTArray<Segment>& debugSegments() {
4071 return fSegments;
4072 }
4073#endif
4074
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004075#if DEBUG_DUMP
4076 void dump() {
4077 int i;
4078 const char className[] = "Contour";
4079 const int tab = 4;
4080 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
4081 for (i = 0; i < fSegments.count(); ++i) {
4082 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
4083 className, i);
4084 fSegments[i].dump();
4085 }
4086 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
4087 tab + sizeof(className), className,
4088 fBounds.fLeft, fBounds.fTop,
4089 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004090 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
4091 className, fContainsIntercepts);
4092 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
4093 className, fContainsCurves);
4094 }
4095#endif
4096
caryclark@google.com027de222012-07-12 12:52:50 +00004097#if DEBUG_ACTIVE_SPANS
4098 void debugShowActiveSpans() {
4099 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004100 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00004101 }
4102 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004103
4104 void validateActiveSpans() {
4105 for (int index = 0; index < fSegments.count(); ++index) {
4106 fSegments[index].validateActiveSpans();
4107 }
4108 }
caryclark@google.com027de222012-07-12 12:52:50 +00004109#endif
4110
caryclark@google.com729e1c42012-11-21 21:36:34 +00004111#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004112 int debugShowWindingValues(int totalSegments, int ofInterest) {
4113 int count = fSegments.count();
4114 int sum = 0;
4115 for (int index = 0; index < count; ++index) {
4116 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
4117 }
4118 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
4119 return sum;
4120 }
4121
4122 static void debugShowWindingValues(SkTDArray<Contour*>& contourList) {
4123 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
4124 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
4125 int ofInterest = 1 << 5 | 1 << 8;
4126 int total = 0;
4127 int index;
4128 for (index = 0; index < contourList.count(); ++index) {
4129 total += contourList[index]->segments().count();
4130 }
4131 int sum = 0;
4132 for (index = 0; index < contourList.count(); ++index) {
4133 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
4134 }
4135 // SkDebugf("%s total=%d\n", __FUNCTION__, sum);
4136 }
4137#endif
4138
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004139protected:
4140 void setBounds() {
4141 int count = fSegments.count();
4142 if (count == 0) {
4143 SkDebugf("%s empty contour\n", __FUNCTION__);
4144 SkASSERT(0);
4145 // FIXME: delete empty contour?
4146 return;
4147 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004148 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004149 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004150 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004151 }
4152 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004153
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004154private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004155 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004156 SkTDArray<Segment*> fSortedSegments;
4157 int fFirstSorted;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004158 SkTDArray<Coincidence> fCoincidences;
4159 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004160 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004161 bool fContainsIntercepts;
4162 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004163 bool fOperand; // true for the second argument to a binary operator
4164 bool fXor;
caryclark@google.com4eeda372012-12-06 21:47:48 +00004165 bool fOppXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004166#if DEBUG_DUMP
4167 int fID;
4168#endif
4169};
4170
4171class EdgeBuilder {
4172public:
4173
caryclark@google.comf839c032012-10-26 21:03:50 +00004174EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
4175 : fPath(path.nativePath())
4176 , fContours(contours)
4177{
4178 init();
4179}
4180
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004181EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00004182 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004183 , fContours(contours)
4184{
caryclark@google.comf839c032012-10-26 21:03:50 +00004185 init();
4186}
4187
4188void init() {
4189 fCurrentContour = NULL;
4190 fOperand = false;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004191 fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004192#if DEBUG_DUMP
4193 gContourID = 0;
4194 gSegmentID = 0;
4195#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00004196 fSecondHalf = preFetch();
4197}
4198
4199void addOperand(const SkPath& path) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004200 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
4201 fPathVerbs.pop();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004202 fPath = &path;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004203 fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004204 preFetch();
4205}
4206
4207void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004208 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004209 complete();
4210 if (fCurrentContour && !fCurrentContour->segments().count()) {
4211 fContours.pop_back();
4212 }
4213 // correct pointers in contours since fReducePts may have moved as it grew
4214 int cIndex = 0;
4215 int extraCount = fExtra.count();
4216 SkASSERT(extraCount == 0 || fExtra[0] == -1);
4217 int eIndex = 0;
4218 int rIndex = 0;
4219 while (++eIndex < extraCount) {
4220 int offset = fExtra[eIndex];
4221 if (offset < 0) {
4222 ++cIndex;
4223 continue;
4224 }
4225 fCurrentContour = &fContours[cIndex];
4226 rIndex += fCurrentContour->updateSegment(offset - 1,
4227 &fReducePts[rIndex]);
4228 }
4229 fExtra.reset(); // we're done with this
4230}
4231
4232ShapeOpMask xorMask() const {
caryclark@google.com729e1c42012-11-21 21:36:34 +00004233 return fXorMask[fOperand];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004234}
4235
4236protected:
4237
4238void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004239 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004240 fCurrentContour->complete();
4241 fCurrentContour = NULL;
4242 }
4243}
4244
caryclark@google.com235f56a2012-09-14 14:19:30 +00004245// FIXME:remove once we can access path pts directly
4246int preFetch() {
4247 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004248 SkPoint pts[4];
4249 SkPath::Verb verb;
4250 do {
4251 verb = iter.next(pts);
4252 *fPathVerbs.append() = verb;
4253 if (verb == SkPath::kMove_Verb) {
4254 *fPathPts.append() = pts[0];
4255 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
4256 fPathPts.append(verb, &pts[1]);
4257 }
4258 } while (verb != SkPath::kDone_Verb);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004259 return fPathVerbs.count() - 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004260}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004261
caryclark@google.com235f56a2012-09-14 14:19:30 +00004262void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004263 SkPath::Verb reducedVerb;
4264 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004265 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004266 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004267 const SkPoint* finalCurveStart = NULL;
4268 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004269 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004270 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
4271 switch (verb) {
4272 case SkPath::kMove_Verb:
4273 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004274 if (!fCurrentContour) {
4275 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004276 fCurrentContour->setOperand(fOperand);
caryclark@google.com729e1c42012-11-21 21:36:34 +00004277 fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004278 *fExtra.append() = -1; // start new contour
4279 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004280 finalCurveEnd = pointsPtr++;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004281 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004282 case SkPath::kLine_Verb:
4283 // skip degenerate points
4284 if (pointsPtr[-1].fX != pointsPtr[0].fX
4285 || pointsPtr[-1].fY != pointsPtr[0].fY) {
4286 fCurrentContour->addLine(&pointsPtr[-1]);
4287 }
4288 break;
4289 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00004290
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004291 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
4292 if (reducedVerb == 0) {
4293 break; // skip degenerate points
4294 }
4295 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00004296 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004297 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004298 break;
4299 }
4300 fCurrentContour->addQuad(&pointsPtr[-1]);
4301 break;
4302 case SkPath::kCubic_Verb:
4303 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
4304 if (reducedVerb == 0) {
4305 break; // skip degenerate points
4306 }
4307 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004308 *fExtra.append() =
4309 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004310 break;
4311 }
4312 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004313 *fExtra.append() =
4314 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004315 break;
4316 }
4317 fCurrentContour->addCubic(&pointsPtr[-1]);
4318 break;
4319 case SkPath::kClose_Verb:
4320 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004321 if (finalCurveStart && finalCurveEnd
4322 && *finalCurveStart != *finalCurveEnd) {
4323 *fReducePts.append() = *finalCurveStart;
4324 *fReducePts.append() = *finalCurveEnd;
4325 *fExtra.append() =
4326 fCurrentContour->addLine(fReducePts.end() - 2);
4327 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004328 complete();
caryclark@google.com31143cf2012-11-09 22:14:19 +00004329 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004330 default:
4331 SkDEBUGFAIL("bad verb");
4332 return;
4333 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004334 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004335 pointsPtr += verb;
4336 SkASSERT(fCurrentContour);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004337 nextVerb:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004338 if (verbPtr == endOfFirstHalf) {
4339 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004340 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004341 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004342}
4343
4344private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004345 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004346 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004347 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004348 Contour* fCurrentContour;
4349 SkTArray<Contour>& fContours;
4350 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004351 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com729e1c42012-11-21 21:36:34 +00004352 ShapeOpMask fXorMask[2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004353 int fSecondHalf;
4354 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004355};
4356
4357class Work {
4358public:
4359 enum SegmentType {
4360 kHorizontalLine_Segment = -1,
4361 kVerticalLine_Segment = 0,
4362 kLine_Segment = SkPath::kLine_Verb,
4363 kQuad_Segment = SkPath::kQuad_Verb,
4364 kCubic_Segment = SkPath::kCubic_Verb,
4365 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004366
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004367 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4368 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4369 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004370
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004371 // FIXME: does it make sense to write otherIndex now if we're going to
4372 // fix it up later?
4373 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004374 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004375 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004376
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004377 // Avoid collapsing t values that are close to the same since
4378 // we walk ts to describe consecutive intersections. Since a pair of ts can
4379 // be nearly equal, any problems caused by this should be taken care
4380 // of later.
4381 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004382 int addT(double newT, const Work& other) {
4383 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004384 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004385
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004386 bool advance() {
4387 return ++fIndex < fLast;
4388 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004389
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004390 SkScalar bottom() const {
4391 return bounds().fBottom;
4392 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004393
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004394 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004395 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004396 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004397
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004398 const SkPoint* cubic() const {
4399 return fCubic;
4400 }
4401
4402 void init(Contour* contour) {
4403 fContour = contour;
4404 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004405 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004406 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004407
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004408 bool isAdjacent(const Work& next) {
4409 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4410 }
4411
4412 bool isFirstLast(const Work& next) {
4413 return fContour == next.fContour && fIndex == 0
4414 && next.fIndex == fLast - 1;
4415 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004416
4417 SkScalar left() const {
4418 return bounds().fLeft;
4419 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004420
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004421 void promoteToCubic() {
4422 fCubic[0] = pts()[0];
4423 fCubic[2] = pts()[1];
4424 fCubic[3] = pts()[2];
4425 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4426 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4427 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4428 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4429 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004430
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004431 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004432 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004433 }
4434
4435 SkScalar right() const {
4436 return bounds().fRight;
4437 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004438
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004439 ptrdiff_t segmentIndex() const {
4440 return fIndex;
4441 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004442
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004443 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004444 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004445 SegmentType type = (SegmentType) segment.verb();
4446 if (type != kLine_Segment) {
4447 return type;
4448 }
4449 if (segment.isHorizontal()) {
4450 return kHorizontalLine_Segment;
4451 }
4452 if (segment.isVertical()) {
4453 return kVerticalLine_Segment;
4454 }
4455 return kLine_Segment;
4456 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004457
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004458 bool startAfter(const Work& after) {
4459 fIndex = after.fIndex;
4460 return advance();
4461 }
4462
4463 SkScalar top() const {
4464 return bounds().fTop;
4465 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004466
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004467 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004468 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004469 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004470
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004471 SkScalar x() const {
4472 return bounds().fLeft;
4473 }
4474
4475 bool xFlipped() const {
4476 return x() != pts()[0].fX;
4477 }
4478
4479 SkScalar y() const {
4480 return bounds().fTop;
4481 }
4482
4483 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004484 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004485 }
4486
4487protected:
4488 Contour* fContour;
4489 SkPoint fCubic[4];
4490 int fIndex;
4491 int fLast;
4492};
4493
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004494#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004495static void debugShowLineIntersection(int pts, const Work& wt,
4496 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004497 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004498 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004499 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4500 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4501 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4502 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004503 return;
4504 }
4505 SkPoint wtOutPt, wnOutPt;
4506 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4507 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004508 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 +00004509 __FUNCTION__,
4510 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4511 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4512 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004513 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004514 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004515 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004516 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4517 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4518 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004519 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4520 }
4521 SkDebugf("\n");
4522}
4523
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004524static void debugShowQuadLineIntersection(int pts, const Work& wt,
4525 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4526 if (!pts) {
4527 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00004528 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004529 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4530 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004531 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004532 return;
4533 }
4534 SkPoint wtOutPt, wnOutPt;
4535 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4536 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4537 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4538 __FUNCTION__,
4539 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4540 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4541 wtOutPt.fX, wtOutPt.fY);
4542 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004543 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
4544 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004545 }
4546 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4547 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4548 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4549 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004550 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
4551 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004552 }
4553 SkDebugf("\n");
4554}
4555
caryclark@google.coma461ff02012-10-11 12:54:23 +00004556static void debugShowQuadIntersection(int pts, const Work& wt,
4557 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4558 if (!pts) {
4559 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
4560 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
4561 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00004562 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00004563 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004564 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00004565 return;
4566 }
4567 SkPoint wtOutPt, wnOutPt;
4568 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4569 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4570 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4571 __FUNCTION__,
4572 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4573 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4574 wtOutPt.fX, wtOutPt.fY);
4575 if (pts == 2) {
4576 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
4577 }
4578 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4579 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4580 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
4581 wnOutPt.fX, wnOutPt.fY);
4582 if (pts == 2) {
4583 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004584 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004585 SkDebugf("\n");
4586}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004587#else
4588static void debugShowLineIntersection(int , const Work& ,
4589 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004590}
caryclark@google.coma461ff02012-10-11 12:54:23 +00004591
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004592static void debugShowQuadLineIntersection(int , const Work& ,
4593 const Work& , const double [2], const double [2]) {
4594}
4595
caryclark@google.coma461ff02012-10-11 12:54:23 +00004596static void debugShowQuadIntersection(int , const Work& ,
4597 const Work& , const double [2], const double [2]) {
4598}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004599#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004600
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004601static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004602
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004603 if (test != next) {
4604 if (test->bounds().fBottom < next->bounds().fTop) {
4605 return false;
4606 }
4607 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
4608 return true;
4609 }
4610 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004611 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004612 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004613 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004614 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004615 Work wn;
4616 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004617 if (test == next && !wn.startAfter(wt)) {
4618 continue;
4619 }
4620 do {
4621 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
4622 continue;
4623 }
4624 int pts;
4625 Intersections ts;
4626 bool swap = false;
4627 switch (wt.segmentType()) {
4628 case Work::kHorizontalLine_Segment:
4629 swap = true;
4630 switch (wn.segmentType()) {
4631 case Work::kHorizontalLine_Segment:
4632 case Work::kVerticalLine_Segment:
4633 case Work::kLine_Segment: {
4634 pts = HLineIntersect(wn.pts(), wt.left(),
4635 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004636 debugShowLineIntersection(pts, wt, wn,
4637 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004638 break;
4639 }
4640 case Work::kQuad_Segment: {
4641 pts = HQuadIntersect(wn.pts(), wt.left(),
4642 wt.right(), wt.y(), wt.xFlipped(), ts);
4643 break;
4644 }
4645 case Work::kCubic_Segment: {
4646 pts = HCubicIntersect(wn.pts(), wt.left(),
4647 wt.right(), wt.y(), wt.xFlipped(), ts);
4648 break;
4649 }
4650 default:
4651 SkASSERT(0);
4652 }
4653 break;
4654 case Work::kVerticalLine_Segment:
4655 swap = true;
4656 switch (wn.segmentType()) {
4657 case Work::kHorizontalLine_Segment:
4658 case Work::kVerticalLine_Segment:
4659 case Work::kLine_Segment: {
4660 pts = VLineIntersect(wn.pts(), wt.top(),
4661 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004662 debugShowLineIntersection(pts, wt, wn,
4663 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004664 break;
4665 }
4666 case Work::kQuad_Segment: {
4667 pts = VQuadIntersect(wn.pts(), wt.top(),
4668 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4669 break;
4670 }
4671 case Work::kCubic_Segment: {
4672 pts = VCubicIntersect(wn.pts(), wt.top(),
4673 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4674 break;
4675 }
4676 default:
4677 SkASSERT(0);
4678 }
4679 break;
4680 case Work::kLine_Segment:
4681 switch (wn.segmentType()) {
4682 case Work::kHorizontalLine_Segment:
4683 pts = HLineIntersect(wt.pts(), wn.left(),
4684 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004685 debugShowLineIntersection(pts, wt, wn,
4686 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004687 break;
4688 case Work::kVerticalLine_Segment:
4689 pts = VLineIntersect(wt.pts(), wn.top(),
4690 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004691 debugShowLineIntersection(pts, wt, wn,
4692 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004693 break;
4694 case Work::kLine_Segment: {
4695 pts = LineIntersect(wt.pts(), wn.pts(), ts);
4696 debugShowLineIntersection(pts, wt, wn,
4697 ts.fT[1], ts.fT[0]);
4698 break;
4699 }
4700 case Work::kQuad_Segment: {
4701 swap = true;
4702 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004703 debugShowQuadLineIntersection(pts, wn, wt,
4704 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004705 break;
4706 }
4707 case Work::kCubic_Segment: {
4708 swap = true;
4709 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
4710 break;
4711 }
4712 default:
4713 SkASSERT(0);
4714 }
4715 break;
4716 case Work::kQuad_Segment:
4717 switch (wn.segmentType()) {
4718 case Work::kHorizontalLine_Segment:
4719 pts = HQuadIntersect(wt.pts(), wn.left(),
4720 wn.right(), wn.y(), wn.xFlipped(), ts);
4721 break;
4722 case Work::kVerticalLine_Segment:
4723 pts = VQuadIntersect(wt.pts(), wn.top(),
4724 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4725 break;
4726 case Work::kLine_Segment: {
4727 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004728 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004729 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004730 break;
4731 }
4732 case Work::kQuad_Segment: {
4733 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004734 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004735 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004736 break;
4737 }
4738 case Work::kCubic_Segment: {
4739 wt.promoteToCubic();
4740 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
4741 break;
4742 }
4743 default:
4744 SkASSERT(0);
4745 }
4746 break;
4747 case Work::kCubic_Segment:
4748 switch (wn.segmentType()) {
4749 case Work::kHorizontalLine_Segment:
4750 pts = HCubicIntersect(wt.pts(), wn.left(),
4751 wn.right(), wn.y(), wn.xFlipped(), ts);
4752 break;
4753 case Work::kVerticalLine_Segment:
4754 pts = VCubicIntersect(wt.pts(), wn.top(),
4755 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4756 break;
4757 case Work::kLine_Segment: {
4758 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
4759 break;
4760 }
4761 case Work::kQuad_Segment: {
4762 wn.promoteToCubic();
4763 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
4764 break;
4765 }
4766 case Work::kCubic_Segment: {
4767 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4768 break;
4769 }
4770 default:
4771 SkASSERT(0);
4772 }
4773 break;
4774 default:
4775 SkASSERT(0);
4776 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004777 if (!foundCommonContour && pts > 0) {
4778 test->addCross(next);
4779 next->addCross(test);
4780 foundCommonContour = true;
4781 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004782 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004783 if (pts == 2) {
4784 if (wn.segmentType() <= Work::kLine_Segment
4785 && wt.segmentType() <= Work::kLine_Segment) {
4786 wt.addCoincident(wn, ts, swap);
4787 continue;
4788 }
4789 if (wn.segmentType() == Work::kQuad_Segment
4790 && wt.segmentType() == Work::kQuad_Segment
4791 && ts.coincidentUsed() == 2) {
4792 wt.addCoincident(wn, ts, swap);
4793 continue;
4794 }
4795
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004796 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004797 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004798 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4799 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004800 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4801 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004802 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4803 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004804 }
4805 } while (wn.advance());
4806 } while (wt.advance());
4807 return true;
4808}
4809
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004810// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004811// see if coincidence is formed by clipping non-concident segments
caryclark@google.com4eeda372012-12-06 21:47:48 +00004812static void coincidenceCheck(SkTDArray<Contour*>& contourList, int total) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004813 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004814 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004815 Contour* contour = contourList[cIndex];
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004816 contour->resolveCoincidence(contourList);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004817 }
4818 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4819 Contour* contour = contourList[cIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004820 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004821 }
4822}
4823
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004824// project a ray from the top of the contour up and see if it hits anything
4825// note: when we compute line intersections, we keep track of whether
4826// two contours touch, so we need only look at contours not touching this one.
4827// OPTIMIZATION: sort contourList vertically to avoid linear walk
4828static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com31143cf2012-11-09 22:14:19 +00004829 const Segment* current, int index, int endIndex, bool opp) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004830 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004831 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004832 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00004833 const Segment* test = NULL;
4834 int tIndex;
4835 double tHit;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004836 for (int cTest = 0; cTest < contourCount; ++cTest) {
4837 Contour* contour = contourList[cTest];
caryclark@google.com7fce0de2012-11-29 14:31:50 +00004838 if ((contour->operand() ^ current->operand()) != opp) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004839 continue;
4840 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004841 if (basePt.fY < contour->bounds().fTop) {
4842 continue;
4843 }
4844 if (bestY > contour->bounds().fBottom) {
4845 continue;
4846 }
caryclark@google.com47580692012-07-23 12:14:49 +00004847 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
4848 if (next) {
4849 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004850 }
caryclark@google.com47580692012-07-23 12:14:49 +00004851 }
4852 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00004853 return 0;
4854 }
4855 int winding, windValue;
4856 // If the ray hit the end of a span, we need to construct the wheel of
4857 // angles to find the span closest to the ray -- even if there are just
4858 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004859 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004860 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00004861 SkTDArray<Angle> angles;
4862 int end = test->nextSpan(tIndex, 1);
4863 if (end < 0) {
4864 end = test->nextSpan(tIndex, -1);
4865 }
4866 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00004867 SkASSERT(angles.count() > 0);
4868 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
4869#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00004870 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00004871#endif
4872 return 0;
4873 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004874 test->buildAngles(tIndex, angles, false);
caryclark@google.com47580692012-07-23 12:14:49 +00004875 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00004876 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00004877 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004878 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00004879 // hour after 6 o'clock
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004880 (void) Segment::SortAngles(angles, sorted);
caryclark@google.com47580692012-07-23 12:14:49 +00004881#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004882 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00004883#endif
4884 // walk the sorted angle fan to find the lowest angle
4885 // above the base point. Currently, the first angle in the sorted array
4886 // is 12 noon or an earlier hour (the next counterclockwise)
4887 int count = sorted.count();
4888 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004889 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00004890 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004891 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00004892 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00004893 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004894 if (angle->unsortable()) {
4895 continue;
4896 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004897 if (baseMatches && angle->isHorizontal()) {
4898 continue;
4899 }
4900 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00004901 test = angle->segment();
4902 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
4903 const SkPoint* pts = test->pts();
4904 indexDx = pts[2].fX - pts[1].fX - indexDx;
4905 }
caryclark@google.com47580692012-07-23 12:14:49 +00004906 if (indexDx < 0) {
4907 left = index;
4908 } else if (indexDx > 0) {
4909 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00004910 int previous = index - 1;
4911 if (previous < 0) {
4912 previous = count - 1;
4913 }
4914 const Angle* prev = sorted[previous];
4915 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
4916#if DEBUG_SORT
4917 SkDebugf("%s use prev\n", __FUNCTION__);
4918#endif
4919 right = previous;
4920 }
caryclark@google.com47580692012-07-23 12:14:49 +00004921 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004922 } else {
4923 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004924 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004925 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004926 if (left < 0 && right < 0) {
4927 left = mid;
4928 }
caryclark@google.com47580692012-07-23 12:14:49 +00004929 SkASSERT(left >= 0 || right >= 0);
4930 if (left < 0) {
4931 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004932 } else if (left >= 0 && mid >= 0 && right >= 0
4933 && sorted[mid]->sign() == sorted[right]->sign()) {
4934 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004935 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004936 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004937 test = angle->segment();
4938 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004939 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004940 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004941#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004942 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4943 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004944#endif
4945 } else {
4946 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004947 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004948 windValue = test->windValue(tIndex);
4949#if DEBUG_WINDING
4950 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4951 windValue);
4952#endif
4953 }
4954 // see if a + change in T results in a +/- change in X (compute x'(T))
4955 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004956 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4957 const SkPoint* pts = test->pts();
4958 dx = pts[2].fX - pts[1].fX - dx;
4959 }
caryclark@google.com47580692012-07-23 12:14:49 +00004960#if DEBUG_WINDING
4961 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4962#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004963 SkASSERT(dx != 0);
4964 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004965 winding += dx > 0 ? -windValue : windValue;
4966#if DEBUG_WINDING
4967 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4968#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004969 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004970 return winding;
4971}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004972
caryclark@google.com24bec792012-08-20 12:43:57 +00004973static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4974 int contourCount = contourList.count();
4975 Segment* result;
4976 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4977 Contour* contour = contourList[cIndex];
4978 result = contour->undoneSegment(start, end);
4979 if (result) {
4980 return result;
4981 }
4982 }
4983 return NULL;
4984}
4985
4986
4987
caryclark@google.com31143cf2012-11-09 22:14:19 +00004988static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004989 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004990 Span* span;
4991 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004992 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4993 Segment* segment = backPtr.fOther;
4994 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004995 SkTDArray<Angle> angles;
4996 int done = 0;
4997 if (segment->activeAngle(tIndex, done, angles)) {
4998 Angle* last = angles.end() - 1;
4999 tIndex = last->start();
5000 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005001 #if TRY_ROTATE
5002 *chase.insert(0) = span;
5003 #else
5004 *chase.append() = span;
5005 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005006 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005007 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005008 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00005009 continue;
5010 }
5011 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005012 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00005013#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005014 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00005015#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005016 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005017 continue;
5018 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005019 // find first angle, initialize winding to computed fWindSum
5020 int firstIndex = -1;
5021 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005022 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005023 do {
5024 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005025 segment = angle->segment();
5026 winding = segment->windSum(angle);
5027 } while (winding == SK_MinS32);
5028 int spanWinding = segment->spanSign(angle->start(), angle->end());
5029 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005030 SkDebugf("%s winding=%d spanWinding=%d\n",
5031 __FUNCTION__, winding, spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00005032 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005033 // turn span winding into contour winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005034 if (spanWinding * winding < 0) {
5035 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005036 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005037 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005038 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005039 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005040 // we care about first sign and whether wind sum indicates this
5041 // edge is inside or outside. Maybe need to pass span winding
5042 // or first winding or something into this function?
5043 // advance to first undone angle, then return it and winding
5044 // (to set whether edges are active or not)
5045 int nextIndex = firstIndex + 1;
5046 int angleCount = sorted.count();
5047 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005048 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00005049 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00005050 do {
5051 SkASSERT(nextIndex != firstIndex);
5052 if (nextIndex == angleCount) {
5053 nextIndex = 0;
5054 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005055 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00005056 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005057 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00005058 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005059 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00005060 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
5061 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005062 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005063 tIndex = angle->start();
5064 endIndex = angle->end();
5065 int lesser = SkMin32(tIndex, endIndex);
5066 const Span& nextSpan = segment->span(lesser);
5067 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005068#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00005069 // FIXME: this be wrong. assign startWinding if edge is in
5070 // same direction. If the direction is opposite, winding to
5071 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00005072 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005073 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005074 }
caryclark@google.com59823f72012-08-09 18:17:47 +00005075 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005076#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005077 break;
5078 }
5079 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005080 #if TRY_ROTATE
5081 *chase.insert(0) = span;
5082 #else
5083 *chase.append() = span;
5084 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005085 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005086 }
5087 return NULL;
5088}
5089
caryclark@google.com027de222012-07-12 12:52:50 +00005090#if DEBUG_ACTIVE_SPANS
5091static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005092 int index;
5093 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00005094 contourList[index]->debugShowActiveSpans();
5095 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005096 for (index = 0; index < contourList.count(); ++ index) {
5097 contourList[index]->validateActiveSpans();
5098 }
caryclark@google.com027de222012-07-12 12:52:50 +00005099}
5100#endif
5101
caryclark@google.com27c449a2012-07-27 18:26:38 +00005102static bool windingIsActive(int winding, int spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005103 // FIXME: !spanWinding test must be superflorous, true?
caryclark@google.com27c449a2012-07-27 18:26:38 +00005104 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
5105 && (!winding || !spanWinding || winding == -spanWinding);
5106}
5107
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005108static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comf839c032012-10-26 21:03:50 +00005109 int& endIndex, SkPoint& topLeft) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005110 Segment* result;
5111 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00005112 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005113 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00005114 Segment* topStart = NULL;
5115 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5116 Contour* contour = contourList[cIndex];
5117 const Bounds& bounds = contour->bounds();
5118 if (bounds.fBottom < topLeft.fY) {
5119 continue;
5120 }
5121 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
5122 continue;
5123 }
5124 Segment* test = contour->topSortableSegment(topLeft, bestXY);
5125 if (test) {
5126 topStart = test;
5127 }
5128 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005129 if (!topStart) {
5130 return NULL;
5131 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005132 topLeft = bestXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005133 result = topStart->findTop(index, endIndex);
5134 } while (!result);
5135 return result;
5136}
caryclark@google.com31143cf2012-11-09 22:14:19 +00005137
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005138static int updateWindings(const Segment* current, int index, int endIndex, int& spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005139 int lesser = SkMin32(index, endIndex);
5140 spanWinding = current->spanSign(index, endIndex);
5141 int winding = current->windSum(lesser);
5142 bool inner = useInnerWinding(winding - spanWinding, winding);
5143#if DEBUG_WINDING
5144 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
5145 " inner=%d result=%d\n",
5146 __FUNCTION__, current->debugID(), current->t(lesser),
5147 spanWinding, winding, SkSign32(index - endIndex),
5148 useInnerWinding(winding - spanWinding, winding),
5149 inner ? winding - spanWinding : winding);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005150#endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005151 if (inner) {
5152 winding -= spanWinding;
5153 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005154 return winding;
5155}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005156
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005157// Each segment may have an inside or an outside. Segments contained within
5158// winding may have insides on either side, and form a contour that should be
5159// ignored. Segments that are coincident with opposing direction segments may
5160// have outsides on either side, and should also disappear.
5161// 'Normal' segments will have one inside and one outside. Subsequent connections
5162// when winding should follow the intersection direction. If more than one edge
5163// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005164 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00005165 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005166// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005167static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005168 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005169 bool unsortable = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005170 bool closable = true;
5171 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com15fa1382012-05-07 20:49:36 +00005172 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005173 int index, endIndex;
caryclark@google.com31143cf2012-11-09 22:14:19 +00005174 // iterates while top is unsortable
caryclark@google.comf839c032012-10-26 21:03:50 +00005175 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005176 if (!current) {
5177 break;
5178 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005179 int contourWinding;
5180 if (firstContour) {
5181 contourWinding = 0;
5182 firstContour = false;
5183 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00005184 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005185 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00005186 if (sumWinding == SK_MinS32) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00005187 sumWinding = current->computeSum(index, endIndex, false);
caryclark@google.com200c2112012-08-03 15:05:04 +00005188 }
5189 if (sumWinding == SK_MinS32) {
5190 contourWinding = innerContourCheck(contourList, current,
caryclark@google.com31143cf2012-11-09 22:14:19 +00005191 index, endIndex, false);
caryclark@google.com200c2112012-08-03 15:05:04 +00005192 } else {
5193 contourWinding = sumWinding;
5194 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00005195 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
5196 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00005197 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005198 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00005199#if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005200 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n",
5201 __FUNCTION__, sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00005202 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00005203#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005204 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005205#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005206 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005207 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
5208#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005209 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005210 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005211 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005212 // FIXME: needs work. While it works in limited situations, it does
5213 // not always compute winding correctly. Active should be removed and instead
5214 // the initial winding should be correctly passed in so that if the
5215 // inner contour is wound the same way, it never finds an accumulated
5216 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00005217 // other than zero when resolving sorted angles.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005218 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005219 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005220 bool active = windingIsActive(winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005221 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00005222 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
5223 __FUNCTION__, active ? "true" : "false",
5224 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005225 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005226 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005227 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005228 int nextStart = index;
5229 int nextEnd = endIndex;
5230 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005231 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005232 if (!next) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005233 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00005234 && current->verb() != SkPath::kLine_Verb
5235 && !simple.isClosed()) {
5236 current->addCurveTo(index, endIndex, simple, true);
5237 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005238 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005239 break;
5240 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005241 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005242 current = next;
5243 index = nextStart;
5244 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005245 } while (!simple.isClosed()
5246 && ((active && !unsortable) || !current->done()));
5247 if (active) {
5248 if (!simple.isClosed()) {
5249 SkASSERT(unsortable);
5250 int min = SkMin32(index, endIndex);
5251 if (!current->done(min)) {
5252 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.com31143cf2012-11-09 22:14:19 +00005253 current->markDone(SkMin32(index, endIndex),
5254 winding ? winding : spanWinding);
caryclark@google.comf839c032012-10-26 21:03:50 +00005255 }
5256 closable = false;
5257 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005258 simple.close();
5259 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005260 current = findChase(chaseArray, index, endIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005261 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00005262 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005263 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005264 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005265 break;
5266 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005267 winding = updateWindings(current, index, endIndex, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005268 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005269 } while (true);
caryclark@google.comf839c032012-10-26 21:03:50 +00005270 return closable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005271}
5272
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005273// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005274static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005275 Segment* current;
5276 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005277 bool unsortable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00005278 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005279 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005280 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005281 int nextStart = start;
5282 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005283 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00005284 if (!next) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005285 if (simple.hasMove()
5286 && current->verb() != SkPath::kLine_Verb
5287 && !simple.isClosed()) {
5288 current->addCurveTo(start, end, simple, true);
5289 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005290 }
caryclark@google.com24bec792012-08-20 12:43:57 +00005291 break;
5292 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005293 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00005294 current = next;
5295 start = nextStart;
5296 end = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005297 } while (!simple.isClosed());
5298 // FIXME: add unsortable test
5299 if (simple.hasMove()) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005300 simple.close();
5301 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005302 #if DEBUG_ACTIVE_SPANS
5303 debugShowActiveSpans(contourList);
5304 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005305 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005306 return !unsortable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005307}
5308
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005309static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5310 int contourCount = contourList.count();
5311 for (int cTest = 0; cTest < contourCount; ++cTest) {
5312 Contour* contour = contourList[cTest];
5313 contour->fixOtherTIndex();
5314 }
5315}
5316
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005317static void sortSegments(SkTDArray<Contour*>& contourList) {
5318 int contourCount = contourList.count();
5319 for (int cTest = 0; cTest < contourCount; ++cTest) {
5320 Contour* contour = contourList[cTest];
5321 contour->sortSegments();
5322 }
5323}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005324
caryclark@google.com4eeda372012-12-06 21:47:48 +00005325static void makeContourList(SkTArray<Contour>& contours, SkTDArray<Contour*>& list,
5326 bool evenOdd, bool oppEvenOdd) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005327 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005328 if (count == 0) {
5329 return;
5330 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005331 for (int index = 0; index < count; ++index) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00005332 Contour& contour = contours[index];
5333 contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
5334 *list.append() = &contour;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005335 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005336 QSort<Contour>(list.begin(), list.end() - 1);
5337}
5338
caryclark@google.comf839c032012-10-26 21:03:50 +00005339static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005340 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005341}
5342
caryclark@google.comf839c032012-10-26 21:03:50 +00005343 /*
5344 check start and end of each contour
5345 if not the same, record them
5346 match them up
5347 connect closest
5348 reassemble contour pieces into new path
5349 */
5350static void assemble(const PathWrapper& path, PathWrapper& simple) {
5351#if DEBUG_PATH_CONSTRUCTION
5352 SkDebugf("%s\n", __FUNCTION__);
5353#endif
5354 SkTArray<Contour> contours;
5355 EdgeBuilder builder(path, contours);
5356 builder.finish();
5357 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005358 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005359 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00005360 for (outer = 0; outer < count; ++outer) {
5361 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005362 const SkPoint& eStart = eContour.start();
5363 const SkPoint& eEnd = eContour.end();
5364 if (approximatelyEqual(eStart, eEnd)) {
5365 eContour.toPath(simple);
5366 continue;
5367 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005368 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005369 }
5370 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005371 if (count == 0) {
5372 return;
5373 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005374 SkTDArray<int> sLink, eLink;
5375 sLink.setCount(count);
5376 eLink.setCount(count);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005377 SkTDArray<double> sBest, eBest;
5378 sBest.setCount(count);
5379 eBest.setCount(count);
caryclark@google.comf839c032012-10-26 21:03:50 +00005380 int rIndex;
5381 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005382 outer = runs[rIndex];
5383 const Contour& oContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005384 const SkPoint& oStart = oContour.start();
5385 const SkPoint& oEnd = oContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005386 double dx = oEnd.fX - oStart.fX;
5387 double dy = oEnd.fY - oStart.fY;
5388 double dist = dx * dx + dy * dy;
5389 sBest[rIndex] = eBest[rIndex] = dist;
5390 sLink[rIndex] = eLink[rIndex] = rIndex;
5391 }
5392 for (rIndex = 0; rIndex < count - 1; ++rIndex) {
5393 outer = runs[rIndex];
5394 const Contour& oContour = contours[outer];
5395 const SkPoint& oStart = oContour.start();
5396 const SkPoint& oEnd = oContour.end();
5397 double bestStartDist = sBest[rIndex];
5398 double bestEndDist = eBest[rIndex];
5399 for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) {
5400 int inner = runs[iIndex];
5401 const Contour& iContour = contours[inner];
caryclark@google.comf839c032012-10-26 21:03:50 +00005402 const SkPoint& iStart = iContour.start();
5403 const SkPoint& iEnd = iContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005404 double dx = iStart.fX - oStart.fX;
5405 double dy = iStart.fY - oStart.fY;
5406 double dist = dx * dx + dy * dy;
5407 if (bestStartDist > dist) {
5408 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005409 sLink[rIndex] = ~iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005410 sLink[iIndex] = ~rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005411 }
5412 dx = iEnd.fX - oStart.fX;
5413 dy = iEnd.fY - oStart.fY;
5414 dist = dx * dx + dy * dy;
5415 if (bestStartDist > dist) {
5416 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005417 sLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005418 eLink[iIndex] = rIndex;
5419 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005420 dx = iStart.fX - oEnd.fX;
5421 dy = iStart.fY - oEnd.fY;
5422 dist = dx * dx + dy * dy;
5423 if (bestEndDist > dist) {
5424 bestEndDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005425 sLink[iIndex] = rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005426 eLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005427 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005428 dx = iEnd.fX - oEnd.fX;
5429 dy = iEnd.fY - oEnd.fY;
5430 dist = dx * dx + dy * dy;
5431 if (bestEndDist > dist) {
5432 bestEndDist = dist;
5433 eLink[iIndex] = ~rIndex;
5434 eLink[rIndex] = ~iIndex;
5435 }
5436 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005437 }
5438 rIndex = 0;
5439 bool forward = true;
5440 bool first = true;
5441 const SkPoint* startPtr;
5442 int sIndex = sLink[rIndex];
caryclark@google.com0b7da432012-10-31 19:00:20 +00005443 SkASSERT(sIndex != INT_MAX);
5444 sLink[rIndex] = INT_MAX;
5445 int eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005446 if (sIndex < 0) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005447 eIndex = sLink[~sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005448 sLink[~sIndex] = INT_MAX;
5449 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005450 eIndex = eLink[sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005451 eLink[sIndex] = INT_MAX;
5452 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005453 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005454 do {
5455 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005456 outer = runs[rIndex];
5457 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005458 if (first) {
5459 startPtr = &contour.start();
5460 first = false;
5461 simple.deferredMove(startPtr[0]);
5462 }
5463 const SkPoint* endPtr;
5464 if (forward) {
5465 contour.toPartialForward(simple);
5466 endPtr = &contour.end();
5467 } else {
5468 contour.toPartialBackward(simple);
5469 endPtr = &contour.start();
5470 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005471 if (sIndex == eIndex) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005472 simple.close();
5473 first = forward = true;
5474 break;
5475 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005476 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005477 eIndex = eLink[rIndex];
5478 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005479 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005480 if (eIndex >= 0) {
5481 SkASSERT(sLink[eIndex] == rIndex);
5482 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005483 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005484 SkASSERT(eLink[~eIndex] == ~rIndex);
5485 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005486 }
5487 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005488 eIndex = sLink[rIndex];
5489 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005490 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005491 if (eIndex >= 0) {
5492 SkASSERT(eLink[eIndex] == rIndex);
5493 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005494 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005495 SkASSERT(sLink[~eIndex] == ~rIndex);
5496 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005497 }
5498 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005499 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005500 if (rIndex < 0) {
5501 forward ^= 1;
5502 rIndex = ~rIndex;
5503 }
5504 } while (true);
5505 for (rIndex = 0; rIndex < count; ++rIndex) {
5506 if (sLink[rIndex] != INT_MAX) {
5507 break;
5508 }
5509 }
5510 } while (rIndex < count);
5511 SkASSERT(first);
5512}
5513
5514void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005515 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00005516 result.reset();
5517 result.setFillType(SkPath::kEvenOdd_FillType);
5518 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005519
5520 // turn path into list of segments
5521 SkTArray<Contour> contours;
5522 // FIXME: add self-intersecting cubics' T values to segment
5523 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00005524 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005525 SkTDArray<Contour*> contourList;
caryclark@google.com4eeda372012-12-06 21:47:48 +00005526 makeContourList(contours, contourList, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005527 Contour** currentPtr = contourList.begin();
5528 if (!currentPtr) {
5529 return;
5530 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005531 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005532 // find all intersections between segments
5533 do {
5534 Contour** nextPtr = currentPtr;
5535 Contour* current = *currentPtr++;
5536 Contour* next;
5537 do {
5538 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005539 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005540 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005541 // eat through coincident edges
caryclark@google.com4eeda372012-12-06 21:47:48 +00005542 coincidenceCheck(contourList, 0);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00005543 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005544 sortSegments(contourList);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005545#if DEBUG_ACTIVE_SPANS
5546 debugShowActiveSpans(contourList);
5547#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005548 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005549 if (builder.xorMask() == kWinding_Mask
5550 ? !bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00005551 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005552 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00005553 SkPath temp;
5554 temp.setFillType(SkPath::kEvenOdd_FillType);
5555 PathWrapper assembled(temp);
5556 assemble(simple, assembled);
5557 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00005558 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005559}
5560