blob: 3ecb16497176c86f5f4e92c7b3a1663bca165c80 [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.coma461ff02012-10-11 12:54:23 +000028#define PRECISE_T_SORT 1
caryclark@google.comfb51afb2012-10-19 15:54:16 +000029#define SORTABLE_CONTOURS 0 // set to 1 for old code that works most of the time
caryclark@google.comf839c032012-10-26 21:03:50 +000030#define PIN_ADD_T 0
caryclark@google.coma461ff02012-10-11 12:54:23 +000031
caryclark@google.com47580692012-07-23 12:14:49 +000032#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comf839c032012-10-26 21:03:50 +000033#define FORCE_RELEASE 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000034
caryclark@google.comfb51afb2012-10-19 15:54:16 +000035#if FORCE_RELEASE || defined SK_RELEASE // set force release to 1 for multiple thread -- no debugging
caryclark@google.com47580692012-07-23 12:14:49 +000036
37const bool gRunTestsInOneThread = false;
38
39#define DEBUG_ACTIVE_SPANS 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.com47580692012-07-23 12:14:49 +000047#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000048#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000049#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000050
51#else
52
caryclark@google.com47580692012-07-23 12:14:49 +000053const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000054
caryclark@google.comc91dfe42012-10-16 12:06:27 +000055#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000056#define DEBUG_ADD_INTERSECTING_TS 1
57#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000058#define DEBUG_ANGLE 1
59#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000060#define DEBUG_CROSS 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000061#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000062#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000063#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000064#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000065#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000066
67#endif
68
caryclark@google.com6aea33f2012-10-09 14:11:58 +000069#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000070
caryclark@google.comfa0588f2012-04-26 21:01:06 +000071#if DEBUG_DUMP
72static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000073// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000074static int gContourID;
75static int gSegmentID;
76#endif
77
caryclark@google.com8dcf1142012-07-02 20:27:02 +000078#ifndef DEBUG_TEST
79#define DEBUG_TEST 0
80#endif
81
caryclark@google.com32546db2012-08-31 20:55:07 +000082#define MAKE_CONST_LINE(line, pts) \
83 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
84#define MAKE_CONST_QUAD(quad, pts) \
85 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
86 {pts[2].fX, pts[2].fY}}
87#define MAKE_CONST_CUBIC(cubic, pts) \
88 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
89 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
90
caryclark@google.comfa0588f2012-04-26 21:01:06 +000091static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
92 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000093 MAKE_CONST_LINE(aLine, a);
94 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +000095 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
96}
97
98static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
99 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000100 MAKE_CONST_QUAD(aQuad, a);
101 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000102 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000103}
104
caryclark@google.com32546db2012-08-31 20:55:07 +0000105static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000106 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000107 MAKE_CONST_CUBIC(aCubic, a);
108 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000109 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
110}
111
112static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
113 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000114 MAKE_CONST_QUAD(aQuad, a);
115 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000116#define TRY_QUARTIC_SOLUTION 1
117#if TRY_QUARTIC_SOLUTION
118 intersect2(aQuad, bQuad, intersections);
119#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000120 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000121#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000122 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000123}
124
125static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
126 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000127 MAKE_CONST_CUBIC(aCubic, a);
128 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000129 intersect(aCubic, bCubic, intersections);
130 return intersections.fUsed;
131}
132
133static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
134 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000135 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000136 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
137}
138
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000139static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
140 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000141 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000142 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
143}
144
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000145static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
146 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000147 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000148 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
149}
150
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000151static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
152 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000153 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000154 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
155}
156
157static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
158 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000159 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000160 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
161}
162
163static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
164 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000165 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000166 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000167}
168
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000169static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
170 SkScalar , SkScalar , bool , Intersections& ) = {
171 NULL,
172 VLineIntersect,
173 VQuadIntersect,
174 VCubicIntersect
175};
176
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000177static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000178 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000179 double x, y;
180 xy_at_t(line, t, x, y);
181 out->fX = SkDoubleToScalar(x);
182 out->fY = SkDoubleToScalar(y);
183}
184
185static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000186 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000187 double x, y;
188 xy_at_t(quad, t, x, y);
189 out->fX = SkDoubleToScalar(x);
190 out->fY = SkDoubleToScalar(y);
191}
192
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000193static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
194 MAKE_CONST_QUAD(quad, a);
195 xy_at_t(quad, t, out->x, out->y);
196}
197
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000198static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000199 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000200 double x, y;
201 xy_at_t(cubic, t, x, y);
202 out->fX = SkDoubleToScalar(x);
203 out->fY = SkDoubleToScalar(y);
204}
205
206static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
207 NULL,
208 LineXYAtT,
209 QuadXYAtT,
210 CubicXYAtT
211};
212
213static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000214 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000215 double x;
216 xy_at_t(aLine, t, x, *(double*) 0);
217 return SkDoubleToScalar(x);
218}
219
220static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000221 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000222 double x;
223 xy_at_t(quad, t, x, *(double*) 0);
224 return SkDoubleToScalar(x);
225}
226
227static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000228 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000229 double x;
230 xy_at_t(cubic, t, x, *(double*) 0);
231 return SkDoubleToScalar(x);
232}
233
234static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
235 NULL,
236 LineXAtT,
237 QuadXAtT,
238 CubicXAtT
239};
240
241static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000242 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000243 double y;
244 xy_at_t(aLine, t, *(double*) 0, y);
245 return SkDoubleToScalar(y);
246}
247
248static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000249 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000250 double y;
251 xy_at_t(quad, t, *(double*) 0, y);
252 return SkDoubleToScalar(y);
253}
254
255static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000256 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000257 double y;
258 xy_at_t(cubic, t, *(double*) 0, y);
259 return SkDoubleToScalar(y);
260}
261
262static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
263 NULL,
264 LineYAtT,
265 QuadYAtT,
266 CubicYAtT
267};
268
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000269static SkScalar LineDXAtT(const SkPoint a[2], double ) {
270 return a[1].fX - a[0].fX;
271}
272
273static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000274 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000275 double x;
276 dxdy_at_t(quad, t, x, *(double*) 0);
277 return SkDoubleToScalar(x);
278}
279
280static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000281 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000282 double x;
283 dxdy_at_t(cubic, t, x, *(double*) 0);
284 return SkDoubleToScalar(x);
285}
286
287static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
288 NULL,
289 LineDXAtT,
290 QuadDXAtT,
291 CubicDXAtT
292};
293
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000294static void LineSubDivide(const SkPoint a[2], double startT, double endT,
295 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000296 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000297 _Line dst;
298 sub_divide(aLine, startT, endT, dst);
299 sub[0].fX = SkDoubleToScalar(dst[0].x);
300 sub[0].fY = SkDoubleToScalar(dst[0].y);
301 sub[1].fX = SkDoubleToScalar(dst[1].x);
302 sub[1].fY = SkDoubleToScalar(dst[1].y);
303}
304
305static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
306 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000307 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000308 Quadratic dst;
309 sub_divide(aQuad, startT, endT, dst);
310 sub[0].fX = SkDoubleToScalar(dst[0].x);
311 sub[0].fY = SkDoubleToScalar(dst[0].y);
312 sub[1].fX = SkDoubleToScalar(dst[1].x);
313 sub[1].fY = SkDoubleToScalar(dst[1].y);
314 sub[2].fX = SkDoubleToScalar(dst[2].x);
315 sub[2].fY = SkDoubleToScalar(dst[2].y);
316}
317
318static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
319 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000320 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000321 Cubic dst;
322 sub_divide(aCubic, startT, endT, dst);
323 sub[0].fX = SkDoubleToScalar(dst[0].x);
324 sub[0].fY = SkDoubleToScalar(dst[0].y);
325 sub[1].fX = SkDoubleToScalar(dst[1].x);
326 sub[1].fY = SkDoubleToScalar(dst[1].y);
327 sub[2].fX = SkDoubleToScalar(dst[2].x);
328 sub[2].fY = SkDoubleToScalar(dst[2].y);
329 sub[3].fX = SkDoubleToScalar(dst[3].x);
330 sub[3].fY = SkDoubleToScalar(dst[3].y);
331}
332
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000333static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
334 SkPoint []) = {
335 NULL,
336 LineSubDivide,
337 QuadSubDivide,
338 CubicSubDivide
339};
340
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000341static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000342 _Line sub) {
343 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000344 _Line dst;
345 sub_divide(aLine, startT, endT, dst);
346 sub[0] = dst[0];
347 sub[1] = dst[1];
348}
349
350static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000351 Quadratic sub) {
352 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000353 Quadratic dst;
354 sub_divide(aQuad, startT, endT, dst);
355 sub[0] = dst[0];
356 sub[1] = dst[1];
357 sub[2] = dst[2];
358}
359
360static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000361 Cubic sub) {
362 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000363 Cubic dst;
364 sub_divide(aCubic, startT, endT, dst);
365 sub[0] = dst[0];
366 sub[1] = dst[1];
367 sub[2] = dst[2];
368 sub[3] = dst[3];
369}
370
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000371#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000372static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
373 SkRect& bounds) {
374 SkPoint dst[3];
375 QuadSubDivide(a, startT, endT, dst);
376 bounds.fLeft = bounds.fRight = dst[0].fX;
377 bounds.fTop = bounds.fBottom = dst[0].fY;
378 for (int index = 1; index < 3; ++index) {
379 bounds.growToInclude(dst[index].fX, dst[index].fY);
380 }
381}
382
383static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
384 SkRect& bounds) {
385 SkPoint dst[4];
386 CubicSubDivide(a, startT, endT, dst);
387 bounds.fLeft = bounds.fRight = dst[0].fX;
388 bounds.fTop = bounds.fBottom = dst[0].fY;
389 for (int index = 1; index < 4; ++index) {
390 bounds.growToInclude(dst[index].fX, dst[index].fY);
391 }
392}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000393#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000394
caryclark@google.com15fa1382012-05-07 20:49:36 +0000395static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000396 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000397 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000398 Quadratic dst;
399 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000400 if (order == 2) { // quad became line
401 for (int index = 0; index < order; ++index) {
402 SkPoint* pt = reducePts.append();
403 pt->fX = SkDoubleToScalar(dst[index].x);
404 pt->fY = SkDoubleToScalar(dst[index].y);
405 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000406 }
407 return (SkPath::Verb) (order - 1);
408}
409
410static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
411 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000412 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000413 Cubic dst;
414 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000415 if (order == 2 || order == 3) { // cubic became line or quad
416 for (int index = 0; index < order; ++index) {
417 SkPoint* pt = reducePts.append();
418 pt->fX = SkDoubleToScalar(dst[index].x);
419 pt->fY = SkDoubleToScalar(dst[index].y);
420 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000421 }
422 return (SkPath::Verb) (order - 1);
423}
424
caryclark@google.com15fa1382012-05-07 20:49:36 +0000425static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000426 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000427 return isLinear(aQuad, 0, 2);
428}
429
430static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000431 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000432 return isLinear(aCubic, 0, 3);
433}
434
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000435static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000436 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000437 double x[2];
438 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000439 xy_at_t(aLine, endT, x[1], *(double*) 0);
440 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000441}
442
443static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000444 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000445 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000446}
447
448static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000449 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000450 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000451}
452
453static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
454 NULL,
455 LineLeftMost,
456 QuadLeftMost,
457 CubicLeftMost
458};
459
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000460#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000461static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
462 Intersections& intersections) {
463 MAKE_CONST_QUAD(aQuad, a);
464 MAKE_CONST_LINE(bLine, b);
465 return intersectRay(aQuad, bLine, intersections);
466}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000467#endif
468
469static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
470 Intersections& intersections) {
471 MAKE_CONST_QUAD(aQuad, a);
472 return intersectRay(aQuad, bLine, intersections);
473}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000474
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000475class Segment;
476
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000477struct Span {
478 Segment* fOther;
479 mutable SkPoint fPt; // lazily computed as needed
480 double fT;
481 double fOtherT; // value at fOther[fOtherIndex].fT
482 int fOtherIndex; // can't be used during intersection
483 int fWindSum; // accumulated from contours surrounding this one
484 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
485 int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
486 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000487 bool fUnsortableStart; // set when start is part of an unsortable pair
488 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.comf839c032012-10-26 21:03:50 +0000489 bool fTiny; // if set, span may still be considered once for edge following
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000490};
491
caryclark@google.com15fa1382012-05-07 20:49:36 +0000492// sorting angles
493// given angles of {dx dy ddx ddy dddx dddy} sort them
494class Angle {
495public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000496 // FIXME: this is bogus for quads and cubics
497 // if the quads and cubics' line from end pt to ctrl pt are coincident,
498 // there's no obvious way to determine the curve ordering from the
499 // derivatives alone. In particular, if one quadratic's coincident tangent
500 // is longer than the other curve, the final control point can place the
501 // longer curve on either side of the shorter one.
502 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
503 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000504
caryclark@google.com32546db2012-08-31 20:55:07 +0000505 /*(
506 for quads and cubics, set up a parameterized line (e.g. LineParameters )
507 for points [0] to [1]. See if point [2] is on that line, or on one side
508 or the other. If it both quads' end points are on the same side, choose
509 the shorter tangent. If the tangents are equal, choose the better second
510 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000511
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000512 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000513 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000514 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000515 double y = dy();
516 double ry = rh.dy();
517 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
518 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000519 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000520 double x = dx();
521 double rx = rh.dx();
522 if (y == 0 && ry == 0 && x * rx < 0) {
523 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000524 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000525 double x_ry = x * ry;
526 double rx_y = rx * y;
527 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000528 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000529 return cmp < 0;
530 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000531 if (approximately_zero(x_ry) && approximately_zero(rx_y)
532 && !approximately_zero_squared(cmp)) {
533 return cmp < 0;
534 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000535 // at this point, the initial tangent line is coincident
536 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) || !approximately_zero(rh.fSide))) {
537 // FIXME: running demo will trigger this assertion
538 // (don't know if commenting out will trigger further assertion or not)
539 // commenting it out allows demo to run in release, though
540 // SkASSERT(fSide != rh.fSide);
541 return fSide < rh.fSide;
542 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000543 // see if either curve can be lengthened and try the tangent compare again
544 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
545 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
546 Angle longer = *this;
547 Angle rhLonger = rh;
548 if (longer.lengthen() | rhLonger.lengthen()) {
549 return longer < rhLonger;
550 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000551 // what if we extend in the other direction?
552 longer = *this;
553 rhLonger = rh;
554 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
555 return longer < rhLonger;
556 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000557 }
caryclark@google.com185c7c42012-10-19 18:26:24 +0000558 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
559 || (rh.fVerb == SkPath::kLine_Verb && approximately_zero(rx) && approximately_zero(ry))) {
560 // See general unsortable comment below. This case can happen when
561 // one line has a non-zero change in t but no change in x and y.
562 fUnsortable = true;
563 rh.fUnsortable = true;
564 return this < &rh; // even with no solution, return a stable sort
565 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000566 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
567 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000568 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000569 // end of the shorter tangent to midway between the end points
570 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000571 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
572 double len = fTangent1.normalSquared();
573 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000574 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000575 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000576 int roots, rroots;
577 bool flip = false;
578 do {
579 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
580 double midX = (q[0].x + q[2].x) / 2;
581 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000582 ray[0] = q[1];
583 ray[1].x = midX;
584 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000585 SkASSERT(ray[0] != ray[1]);
586 roots = QuadRayIntersect(fPts, ray, i);
587 rroots = QuadRayIntersect(rh.fPts, ray, ri);
588 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000589 if (roots == 0 || rroots == 0) {
590 // FIXME: we don't have a solution in this case. The interim solution
591 // is to mark the edges as unsortable, exclude them from this and
592 // future computations, and allow the returned path to be fragmented
593 fUnsortable = true;
594 rh.fUnsortable = true;
595 return this < &rh; // even with no solution, return a stable sort
596 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000597 _Point loc;
598 double best = SK_ScalarInfinity;
599 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000600 int index;
601 for (index = 0; index < roots; ++index) {
602 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000603 dx = loc.x - ray[0].x;
604 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000605 dist = dx * dx + dy * dy;
606 if (best > dist) {
607 best = dist;
608 }
609 }
610 for (index = 0; index < rroots; ++index) {
611 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000612 dx = loc.x - ray[0].x;
613 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000614 dist = dx * dx + dy * dy;
615 if (best > dist) {
616 return fSide < 0;
617 }
618 }
619 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000620 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000621
caryclark@google.com47580692012-07-23 12:14:49 +0000622 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000623 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000624 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000625
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000626 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000627 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000628 }
629
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000630 int end() const {
631 return fEnd;
632 }
633
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000634 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000635 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000636 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000637
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000638 bool lengthen() {
639 int newEnd = fEnd;
640 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
641 fEnd = newEnd;
642 setSpans();
643 return true;
644 }
645 return false;
646 }
647
caryclark@google.coma461ff02012-10-11 12:54:23 +0000648 bool reverseLengthen() {
649 if (fReversed) {
650 return false;
651 }
652 int newEnd = fStart;
653 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
654 fEnd = newEnd;
655 fReversed = true;
656 setSpans();
657 return true;
658 }
659 return false;
660 }
661
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000662 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000663 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000664 fSegment = segment;
665 fStart = start;
666 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000667 fPts = orig;
668 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000669 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000670 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000671 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000672 setSpans();
673 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000674
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000675 void setSpans() {
676 double startT = (*fSpans)[fStart].fT;
677 double endT = (*fSpans)[fEnd].fT;
678 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000679 case SkPath::kLine_Verb:
680 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000681 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000682 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
683 fTangent1.lineEndPoints(l);
caryclark@google.comf839c032012-10-26 21:03:50 +0000684 fUnsortable = dx() == 0 && dy() == 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000685 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000686 break;
687 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000688 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000689 fTangent1.quadEndPoints(fQ, 0, 1);
690 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000691 break;
692 case SkPath::kCubic_Verb:
693 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000694 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000695 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000696 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000697 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000698 default:
699 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000700 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000701 if (fUnsortable) {
702 return;
703 }
704 SkASSERT(fStart != fEnd);
705 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
706 for (int index = fStart; index != fEnd; index += step) {
707 if ((*fSpans)[index].fUnsortableStart) {
708 fUnsortable = true;
709 return;
710 }
711 if (index != fStart && (*fSpans)[index].fUnsortableEnd) {
712 fUnsortable = true;
713 return;
714 }
715 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000716 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000717
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000718 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000719 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000720 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000721
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000722 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000723 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000724 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000725
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000726 const SkTDArray<Span>* spans() const {
727 return fSpans;
728 }
729
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000730 int start() const {
731 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000732 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000733
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000734 bool unsortable() const {
735 return fUnsortable;
736 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000737
caryclark@google.comc899ad92012-08-23 15:24:42 +0000738#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000739 const SkPoint* pts() const {
740 return fPts;
741 }
742
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000743 SkPath::Verb verb() const {
744 return fVerb;
745 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000746
caryclark@google.comc899ad92012-08-23 15:24:42 +0000747 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000748 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000749 }
750#endif
751
caryclark@google.com15fa1382012-05-07 20:49:36 +0000752private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000753 const SkPoint* fPts;
754 Quadratic fQ;
755 SkPath::Verb fVerb;
756 double fSide;
757 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000758 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000759 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000760 int fStart;
761 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000762 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000763 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000764};
765
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000766// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000767struct Bounds : public SkRect {
768 static bool Intersects(const Bounds& a, const Bounds& b) {
769 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
770 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
771 }
772
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000773 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
774 if (left < fLeft) {
775 fLeft = left;
776 }
777 if (top < fTop) {
778 fTop = top;
779 }
780 if (right > fRight) {
781 fRight = right;
782 }
783 if (bottom > fBottom) {
784 fBottom = bottom;
785 }
786 }
787
788 void add(const Bounds& toAdd) {
789 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
790 }
791
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000792 bool isEmpty() {
793 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000794 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000795 || isnan(fLeft) || isnan(fRight)
796 || isnan(fTop) || isnan(fBottom);
797 }
798
799 void setCubicBounds(const SkPoint a[4]) {
800 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000801 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000802 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000803 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
804 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000805 }
806
807 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000808 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000809 _Rect dRect;
810 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000811 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
812 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000813 }
814};
815
caryclark@google.com2ddff932012-08-07 21:25:27 +0000816static bool useInnerWinding(int outerWinding, int innerWinding) {
817 SkASSERT(outerWinding != innerWinding);
818 int absOut = abs(outerWinding);
819 int absIn = abs(innerWinding);
820 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
821 if (outerWinding * innerWinding < 0) {
822#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000823 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000824 outerWinding, innerWinding, result ? "true" : "false");
825#endif
826 }
827 return result;
828}
829
caryclark@google.com235f56a2012-09-14 14:19:30 +0000830static const bool opLookup[][2][2] = {
831 // ==0 !=0
832 // b a b a
833 {{true , false}, {false, true }}, // a - b
834 {{false, false}, {true , true }}, // a & b
835 {{true , true }, {false, false}}, // a | b
836 {{true , true }, {true , true }}, // a ^ b
837};
838
839static bool activeOp(bool angleIsOp, int otherNonZero, ShapeOp op) {
840 return opLookup[op][otherNonZero][angleIsOp];
841}
842
caryclark@google.comf839c032012-10-26 21:03:50 +0000843
844// wrap path to keep track of whether the contour is initialized and non-empty
845class PathWrapper {
846public:
847 PathWrapper(SkPath& path)
848 : fPathPtr(&path)
849 {
850 init();
851 }
852
853 void close() {
854 if (!fHasMove) {
855 return;
856 }
857 bool callClose = isClosed();
858 lineTo();
859 if (fEmpty) {
860 return;
861 }
862 if (callClose) {
863 #if DEBUG_PATH_CONSTRUCTION
864 SkDebugf("path.close();\n");
865 #endif
866 fPathPtr->close();
867 }
868 init();
869 }
870
871 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
872 lineTo();
873 moveTo();
874#if DEBUG_PATH_CONSTRUCTION
875 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
876 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
877#endif
878 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
879 fDefer[0] = fDefer[1] = pt3;
880 fEmpty = false;
881 }
882
883 void deferredLine(const SkPoint& pt) {
884 if (pt == fDefer[1]) {
885 return;
886 }
887 if (changedSlopes(pt)) {
888 lineTo();
889 fDefer[0] = fDefer[1];
890 }
891 fDefer[1] = pt;
892 }
893
894 void deferredMove(const SkPoint& pt) {
895 fMoved = true;
896 fHasMove = true;
897 fEmpty = true;
898 fDefer[0] = fDefer[1] = pt;
899 }
900
901 void deferredMoveLine(const SkPoint& pt) {
902 if (!fHasMove) {
903 deferredMove(pt);
904 }
905 deferredLine(pt);
906 }
907
908 bool hasMove() const {
909 return fHasMove;
910 }
911
912 void init() {
913 fEmpty = true;
914 fHasMove = false;
915 fMoved = false;
916 }
917
918 bool isClosed() const {
919 return !fEmpty && fFirstPt == fDefer[1];
920 }
921
922 void lineTo() {
923 if (fDefer[0] == fDefer[1]) {
924 return;
925 }
926 moveTo();
927 fEmpty = false;
928#if DEBUG_PATH_CONSTRUCTION
929 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
930#endif
931 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
932 fDefer[0] = fDefer[1];
933 }
934
935 const SkPath* nativePath() const {
936 return fPathPtr;
937 }
938
939 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
940 lineTo();
941 moveTo();
942#if DEBUG_PATH_CONSTRUCTION
943 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
944 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
945#endif
946 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
947 fDefer[0] = fDefer[1] = pt2;
948 fEmpty = false;
949 }
950
951protected:
952 bool changedSlopes(const SkPoint& pt) const {
953 if (fDefer[0] == fDefer[1]) {
954 return false;
955 }
956 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
957 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
958 SkScalar lineDx = pt.fX - fDefer[1].fX;
959 SkScalar lineDy = pt.fY - fDefer[1].fY;
960 return deferDx * lineDy != deferDy * lineDx;
961 }
962
963 void moveTo() {
964 if (!fMoved) {
965 return;
966 }
967 fFirstPt = fDefer[0];
968#if DEBUG_PATH_CONSTRUCTION
969 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
970#endif
971 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
972 fMoved = false;
973 }
974
975private:
976 SkPath* fPathPtr;
977 SkPoint fDefer[2];
978 SkPoint fFirstPt;
979 bool fEmpty;
980 bool fHasMove;
981 bool fMoved;
982};
983
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000984class Segment {
985public:
986 Segment() {
987#if DEBUG_DUMP
988 fID = ++gSegmentID;
989#endif
990 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +0000991
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000992 bool operator<(const Segment& rh) const {
993 return fBounds.fTop < rh.fBounds.fTop;
994 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000995
caryclark@google.com9764cc62012-07-12 19:29:45 +0000996 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
997 if (activeAngleInner(index, done, angles)) {
998 return true;
999 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001000 double referenceT = fTs[index].fT;
1001 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001002 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001003 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001004 return true;
1005 }
1006 }
1007 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001008 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001009 return true;
1010 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001011 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001012 return false;
1013 }
1014
caryclark@google.com9764cc62012-07-12 19:29:45 +00001015 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001016 Span* span = &fTs[index];
1017 Segment* other = span->fOther;
1018 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001019 return other->activeAngleInner(oIndex, done, angles);
1020 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001021
caryclark@google.com9764cc62012-07-12 19:29:45 +00001022 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001023 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001024 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001025 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001026 if (upSpan.fWindValue) {
1027 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001028 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001029 done++;
1030 } else if (upSpan.fWindSum != SK_MinS32) {
1031 return true;
1032 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001033 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001034 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001035 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001036 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001037 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001038 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001039 if (downSpan.fWindValue) {
1040 addAngle(angles, index, prev);
1041 if (downSpan.fDone) {
1042 done++;
1043 } else if (downSpan.fWindSum != SK_MinS32) {
1044 return true;
1045 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001046 }
1047 }
1048 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001049 }
1050
caryclark@google.comf839c032012-10-26 21:03:50 +00001051 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001052 SkASSERT(!done());
1053 int count = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001054 result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001055 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001056 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001057 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001058 const Span& span = fTs[index];
1059 if (span.fUnsortableStart | lastUnsortable) {
1060 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001061 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001062 if (!span.fDone | !lastDone) {
1063 const SkPoint& xy = xyAtT(index);
1064 if (result.fY < xy.fY) {
1065 goto next;
1066 }
1067 if (result.fY == xy.fY && result.fX < xy.fX) {
1068 goto next;
1069 }
1070 result = xy;
1071 }
1072 next:
1073 lastDone = span.fDone;
1074 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001075 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001076 SkASSERT(result.fY < SK_ScalarMax);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001077 }
1078
1079 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001080 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001081 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001082#if DEBUG_ANGLE
1083 if (angles.count() > 1) {
1084 SkPoint angle0Pt, newPt;
1085 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1086 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1087 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1088 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1089 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1090 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001091#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001092 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001093 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001094
caryclark@google.com2ddff932012-08-07 21:25:27 +00001095 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001096 double oEnd) {
1097 int tIndex = -1;
1098 int tCount = fTs.count();
1099 int oIndex = -1;
1100 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001101 do {
1102 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001103 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001104 int tIndexStart = tIndex;
1105 do {
1106 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001107 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001108 int oIndexStart = oIndex;
1109 double nextT;
1110 do {
1111 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001112 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001113 double oNextT;
1114 do {
1115 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001116 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001117 // at this point, spans before and after are at:
1118 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1119 // if tIndexStart == 0, no prior span
1120 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001121
caryclark@google.comcc905052012-07-25 20:59:42 +00001122 // advance the span with zero winding
1123 // if the following span exists (not past the end, non-zero winding)
1124 // connect the two edges
1125 if (!fTs[tIndexStart].fWindValue) {
1126 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1127 #if DEBUG_CONCIDENT
1128 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1129 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001130 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1131 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001132 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001133 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001134 }
1135 if (nextT < 1 && fTs[tIndex].fWindValue) {
1136 #if DEBUG_CONCIDENT
1137 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1138 __FUNCTION__, fID, other.fID, tIndex,
1139 fTs[tIndex].fT, xyAtT(tIndex).fX,
1140 xyAtT(tIndex).fY);
1141 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001142 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001143 }
1144 } else {
1145 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1146 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1147 #if DEBUG_CONCIDENT
1148 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1149 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001150 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1151 other.xyAtT(oIndexStart).fY);
1152 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001153 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001154 }
1155 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1156 #if DEBUG_CONCIDENT
1157 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1158 __FUNCTION__, fID, other.fID, oIndex,
1159 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1160 other.xyAtT(oIndex).fY);
1161 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1162 #endif
1163 }
1164 }
1165 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001166
caryclark@google.comcc905052012-07-25 20:59:42 +00001167 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1168 double oEnd) {
1169 // walk this to outsideTs[0]
1170 // walk other to outsideTs[1]
1171 // if either is > 0, add a pointer to the other, copying adjacent winding
1172 int tIndex = -1;
1173 int oIndex = -1;
1174 double tStart = outsideTs[0];
1175 double oStart = outsideTs[1];
1176 do {
1177 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001178 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001179 do {
1180 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001181 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001182 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001183 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001184 }
1185 tStart = fTs[tIndex].fT;
1186 oStart = other.fTs[oIndex].fT;
1187 do {
1188 double nextT;
1189 do {
1190 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001191 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001192 tStart = nextT;
1193 do {
1194 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001195 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001196 oStart = nextT;
1197 if (tStart == 1 && oStart == 1) {
1198 break;
1199 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001200 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001201 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001202 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001203
caryclark@google.com235f56a2012-09-14 14:19:30 +00001204 void addCubic(const SkPoint pts[4], bool operand) {
1205 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001206 fBounds.setCubicBounds(pts);
1207 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001208
1209 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001210 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001211 const SkPoint* ePtr;
1212 int lastT = fTs.count() - 1;
1213 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1214 ePtr = fPts;
1215 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001216 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001217 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1218 ePtr = edge;
1219 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001220 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001221 bool reverse = ePtr == fPts && start != 0;
1222 if (reverse) {
1223 path.deferredMoveLine(ePtr[fVerb]);
1224 switch (fVerb) {
1225 case SkPath::kLine_Verb:
1226 path.deferredLine(ePtr[0]);
1227 break;
1228 case SkPath::kQuad_Verb:
1229 path.quadTo(ePtr[1], ePtr[0]);
1230 break;
1231 case SkPath::kCubic_Verb:
1232 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1233 break;
1234 default:
1235 SkASSERT(0);
1236 }
1237 // return ePtr[0];
1238 } else {
1239 path.deferredMoveLine(ePtr[0]);
1240 switch (fVerb) {
1241 case SkPath::kLine_Verb:
1242 path.deferredLine(ePtr[1]);
1243 break;
1244 case SkPath::kQuad_Verb:
1245 path.quadTo(ePtr[1], ePtr[2]);
1246 break;
1247 case SkPath::kCubic_Verb:
1248 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1249 break;
1250 default:
1251 SkASSERT(0);
1252 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001253 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001254 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001255 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001256 }
1257
caryclark@google.com235f56a2012-09-14 14:19:30 +00001258 void addLine(const SkPoint pts[2], bool operand) {
1259 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001260 fBounds.set(pts, 2);
1261 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001262
caryclark@google.comf839c032012-10-26 21:03:50 +00001263#if 0
1264 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001265 const SkPoint& pt = xyAtT(tIndex);
1266 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001267 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001268 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001269 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001270 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001271#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001272
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001273 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001274 void addOtherT(int index, double otherT, int otherIndex) {
1275 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001276 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001277 if (precisely_less_than_zero(otherT)) {
1278 otherT = 0;
1279 } else if (precisely_greater_than_one(otherT)) {
1280 otherT = 1;
1281 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001282 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001283 span.fOtherT = otherT;
1284 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001285 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001286
caryclark@google.com235f56a2012-09-14 14:19:30 +00001287 void addQuad(const SkPoint pts[3], bool operand) {
1288 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001289 fBounds.setQuadBounds(pts);
1290 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001291
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001292 // Defer all coincident edge processing until
1293 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001294
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001295// no need to be tricky; insert in normal T order
1296// resolve overlapping ts when considering coincidence later
1297
1298 // add non-coincident intersection. Resulting edges are sorted in T.
1299 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001300 // FIXME: in the pathological case where there is a ton of intercepts,
1301 // binary search?
1302 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001303 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001304 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001305 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001306 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001307 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001308 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001309 newT = 1;
1310 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001311 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001312 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001313 // OPTIMIZATION: if there are three or more identical Ts, then
1314 // the fourth and following could be further insertion-sorted so
1315 // that all the edges are clockwise or counterclockwise.
1316 // This could later limit segment tests to the two adjacent
1317 // neighbors, although it doesn't help with determining which
1318 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001319 if (newT < fTs[index].fT) {
1320 insertedAt = index;
1321 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001322 }
1323 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001324 Span* span;
1325 if (insertedAt >= 0) {
1326 span = fTs.insert(insertedAt);
1327 } else {
1328 insertedAt = tCount;
1329 span = fTs.append();
1330 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001331 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001332 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001333 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001334 span->fWindSum = SK_MinS32;
1335 span->fWindValue = 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001336 span->fWindValueOpp = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001337 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001338 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001339 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001340 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001341 span->fUnsortableStart = false;
1342 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001343 if (span - fTs.begin() > 0 && !span[-1].fDone
1344 && !precisely_negative(newT - span[-1].fT)
1345 // && approximately_negative(newT - span[-1].fT)
1346 && xyAtT(&span[-1]) == xyAtT(span)) {
1347 span[-1].fTiny = true;
1348 span[-1].fDone = true;
1349 span[-1].fUnsortableStart = approximately_negative(newT - span[-1].fT)
1350 && approximately_greater_than_one(newT);
1351 ++fDoneSpans;
1352 }
1353 if (fTs.end() - span > 1 && !span->fDone
1354 && !precisely_negative(span[1].fT - newT)
1355 // && approximately_negative(span[1].fT - newT)
1356 && xyAtT(&span[1]) == xyAtT(span)) {
1357 span->fTiny = true;
1358 span->fDone = true;
1359 span->fUnsortableEnd = approximately_negative(span[1].fT - newT)
1360 && approximately_less_than_zero(newT);
1361 ++fDoneSpans;
1362 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001363 return insertedAt;
1364 }
1365
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001366 // set spans from start to end to decrement by one
1367 // note this walks other backwards
1368 // FIMXE: there's probably an edge case that can be constructed where
1369 // two span in one segment are separated by float epsilon on one span but
1370 // not the other, if one segment is very small. For this
1371 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001372 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001373 // sorted? It feels better in such a case to match the span's other span
1374 // pointer since both coincident segments must contain the same spans.
1375 void addTCancel(double startT, double endT, Segment& other,
1376 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001377 SkASSERT(!approximately_negative(endT - startT));
1378 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001379 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001380 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001381 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001382 ++index;
1383 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001384 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001385 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001386 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001387 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001388 Span* test = &fTs[index];
1389 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001390 SkTDArray<double> outsideTs;
1391 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001392 do {
1393 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001394 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001395 double testT = test->fT;
1396 double oTestT = oTest->fT;
1397 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001398 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001399 if (decrement) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001400 if (binary) {
1401 --(span->fWindValueOpp);
1402 } else {
1403 decrementSpan(span);
1404 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001405 } else if (track && span->fT < 1 && oTestT < 1) {
1406 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001407 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001408 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001409 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001410 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001411 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1412 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1413 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001414 while (approximately_negative(otherTMatchStart - oSpan->fT)
1415 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001416 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001417 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001418 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001419 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001420 other.decrementSpan(oSpan);
1421 } else if (track && oSpan->fT < 1 && testT < 1) {
1422 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001423 }
1424 if (!oIndex) {
1425 break;
1426 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001427 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001428 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001429 test = span;
1430 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001431 } while (!approximately_negative(endT - test->fT));
1432 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001433 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001434 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001435 double tStart = outsideTs[0];
1436 double oStart = outsideTs[1];
1437 addCancelOutsides(tStart, oStart, other, oEndT);
1438 int count = outsideTs.count();
1439 if (count > 2) {
1440 double tStart = outsideTs[count - 2];
1441 double oStart = outsideTs[count - 1];
1442 addCancelOutsides(tStart, oStart, other, oEndT);
1443 }
caryclark@google.com18063442012-07-25 12:05:18 +00001444 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001445 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001446 double tStart = oOutsideTs[0];
1447 double oStart = oOutsideTs[1];
1448 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001449 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001450 }
1451
1452 // set spans from start to end to increment the greater by one and decrement
1453 // the lesser
caryclark@google.com235f56a2012-09-14 14:19:30 +00001454 void addTCoincident(const bool isXor, double startT, double endT,
1455 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001456 SkASSERT(!approximately_negative(endT - startT));
1457 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001458 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001459 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001460 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001461 ++index;
1462 }
1463 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001464 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001465 ++oIndex;
1466 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001467 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001468 Span* test = &fTs[index];
1469 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001470 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001471 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001472 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001473 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001474 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001475 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001476 bool decrementThis = (test->fWindValue < oTest->fWindValue) & !isXor;
1477 bool decrementOther = (test->fWindValue >= oTest->fWindValue) & !isXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001478 Span* end = test;
1479 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001480 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001481 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001482 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001483 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001484 if (transfer) {
1485 if (decrementOther) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001486 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001487 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001488 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001489 if (binary) {
1490 ++(end->fWindValueOpp);
1491 } else {
1492 ++(end->fWindValue);
1493 }
caryclark@google.com18063442012-07-25 12:05:18 +00001494 } else if (decrementSpan(end)) {
1495 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001496 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001497 } else if (oTest->fWindValue) {
1498 SkASSERT(!decrementOther);
1499 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1500 TrackOutside(xOutsideTs, end->fT, oStartT);
1501 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001502 }
1503 end = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001504 } while (approximately_negative(end->fT - test->fT));
caryclark@google.com59823f72012-08-09 18:17:47 +00001505 // because of the order in which coincidences are resolved, this and other
1506 // may not have the same intermediate points. Compute the corresponding
1507 // intermediate T values (using this as the master, other as the follower)
1508 // and walk other conditionally -- hoping that it catches up in the end
1509 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001510 Span* oEnd = oTest;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001511 while (!approximately_negative(oEndT - oEnd->fT) && approximately_negative(oEnd->fT - otherTMatch)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00001512 if (transfer) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001513 if (decrementThis) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001514 #ifdef SK_DEBUG
1515 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1516 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001517 if (binary) {
1518 ++(oEnd->fWindValueOpp);
1519 } else {
1520 ++(oEnd->fWindValue);
1521 }
caryclark@google.com18063442012-07-25 12:05:18 +00001522 } else if (other.decrementSpan(oEnd)) {
1523 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001524 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001525 } else if (test->fWindValue) {
1526 SkASSERT(!decrementOther);
1527 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1528 SkASSERT(0); // track for later?
1529 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001530 }
1531 oEnd = &other.fTs[++oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001532 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001533 test = end;
1534 oTest = oEnd;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001535 } while (!approximately_negative(endT - test->fT));
1536 SkASSERT(approximately_negative(oTest->fT - oEndT));
1537 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001538 if (!done()) {
1539 if (outsideTs.count()) {
1540 addCoinOutsides(outsideTs, other, oEndT);
1541 }
1542 if (xOutsideTs.count()) {
1543 addCoinOutsides(xOutsideTs, other, oEndT);
1544 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001545 }
1546 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001547 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001548 }
1549 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001550
caryclark@google.comcc905052012-07-25 20:59:42 +00001551 // FIXME: this doesn't prevent the same span from being added twice
1552 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001553 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001554 int tCount = fTs.count();
1555 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1556 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001557 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001558 break;
1559 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001560 if (approximately_negative(span.fT - t) && span.fOther == &other
1561 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001562#if DEBUG_ADD_T_PAIR
1563 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1564 __FUNCTION__, fID, t, other.fID, otherT);
1565#endif
1566 return;
1567 }
1568 }
caryclark@google.com47580692012-07-23 12:14:49 +00001569#if DEBUG_ADD_T_PAIR
1570 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1571 __FUNCTION__, fID, t, other.fID, otherT);
1572#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001573 int insertedAt = addT(t, &other);
1574 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001575 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001576 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001577 matchWindingValue(insertedAt, t, borrowWind);
1578 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001579 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001580
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001581 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001582 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001583 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1584 addAngle(angles, end, start);
1585 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001586 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001587 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001588 int tIndex = nextExactSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001589 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001590 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001591 }
1592 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001593
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001594 const Bounds& bounds() const {
1595 return fBounds;
1596 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001597
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001598 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001599 double referenceT = fTs[index].fT;
1600 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001601 #if PRECISE_T_SORT
1602 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
1603 buildAnglesInner(lesser, angles);
1604 }
1605 do {
1606 buildAnglesInner(index, angles);
1607 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
1608 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001609 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001610 buildAnglesInner(lesser, angles);
1611 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001612 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001613 buildAnglesInner(index, angles);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001614 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00001615 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001616 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001617
1618 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1619 Span* span = &fTs[index];
1620 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001621 // if there is only one live crossing, and no coincidence, continue
1622 // in the same direction
1623 // if there is coincidence, the only choice may be to reverse direction
1624 // find edge on either side of intersection
1625 int oIndex = span->fOtherIndex;
1626 // if done == -1, prior span has already been processed
1627 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001628 #if PRECISE_T_SORT
1629 int next = other->nextExactSpan(oIndex, step);
1630 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001631 int next = other->nextSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001632 #endif
1633 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001634 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001635 #if PRECISE_T_SORT
1636 next = other->nextExactSpan(oIndex, step);
1637 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001638 next = other->nextSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001639 #endif
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001640 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001641 // add candidate into and away from junction
1642 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001643 }
1644
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001645 // figure out if the segment's ascending T goes clockwise or not
1646 // not enough context to write this as shown
1647 // instead, add all segments meeting at the top
1648 // sort them using buildAngleList
1649 // find the first in the sort
1650 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001651 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001652 SkASSERT(0); // incomplete
1653 return false;
1654 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001655
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001656 // FIXME may not need this at all
1657 // FIXME once I figure out the logic, merge this and too close to call
1658 // NOTE not sure if tiny triangles can ever form at the edge, so until we
1659 // see one, only worry about triangles that happen away from 0 and 1
1660 void collapseTriangles(bool isXor) {
1661 if (fTs.count() < 3) { // require t=0, x, 1 at minimum
1662 return;
1663 }
1664 int lastIndex = 1;
1665 double lastT;
1666 while (approximately_less_than_zero((lastT = fTs[lastIndex].fT))) {
1667 ++lastIndex;
1668 }
1669 if (approximately_greater_than_one(lastT)) {
1670 return;
1671 }
1672 int matchIndex = lastIndex;
1673 do {
1674 Span& match = fTs[++matchIndex];
1675 double matchT = match.fT;
1676 if (approximately_greater_than_one(matchT)) {
1677 return;
1678 }
1679 if (matchT == lastT) {
1680 goto nextSpan;
1681 }
1682 if (approximately_negative(matchT - lastT)) {
1683 Span& last = fTs[lastIndex];
1684 Segment* lOther = last.fOther;
1685 double lT = last.fOtherT;
1686 if (approximately_less_than_zero(lT) || approximately_greater_than_one(lT)) {
1687 goto nextSpan;
1688 }
1689 Segment* mOther = match.fOther;
1690 double mT = match.fOtherT;
1691 if (approximately_less_than_zero(mT) || approximately_greater_than_one(mT)) {
1692 goto nextSpan;
1693 }
1694 // add new point to connect adjacent spurs
1695 int count = lOther->fTs.count();
1696 for (int index = 0; index < count; ++index) {
1697 Span& span = lOther->fTs[index];
1698 if (span.fOther == mOther && approximately_zero(span.fOtherT - mT)) {
1699 goto nextSpan;
1700 }
1701 }
1702 mOther->addTPair(mT, *lOther, lT, false);
1703 // FIXME ? this could go on to detect that spans on mOther, lOther are now coincident
1704 }
1705 nextSpan:
1706 lastIndex = matchIndex;
1707 lastT = matchT;
1708 } while (true);
1709 }
1710
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001711 int computeSum(int startIndex, int endIndex) {
1712 SkTDArray<Angle> angles;
1713 addTwoAngles(startIndex, endIndex, angles);
1714 buildAngles(endIndex, angles);
caryclark@google.comd1688742012-09-18 20:08:37 +00001715 // OPTIMIZATION: check all angles to see if any have computed wind sum
1716 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001717 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001718 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001719#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00001720 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001721#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001722 if (!sortable) {
1723 return SK_MinS32;
1724 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001725 int angleCount = angles.count();
1726 const Angle* angle;
1727 const Segment* base;
1728 int winding;
1729 int firstIndex = 0;
1730 do {
1731 angle = sorted[firstIndex];
1732 base = angle->segment();
1733 winding = base->windSum(angle);
1734 if (winding != SK_MinS32) {
1735 break;
1736 }
1737 if (++firstIndex == angleCount) {
1738 return SK_MinS32;
1739 }
1740 } while (true);
1741 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001742 int spanWinding = base->spanSign(angle);
1743 bool inner = useInnerWinding(winding + spanWinding, winding);
1744 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001745 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001746 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001747 inner ? winding + spanWinding : winding);
1748 #endif
1749 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001750 winding += spanWinding;
1751 }
1752 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001753 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001754 #endif
1755 int nextIndex = firstIndex + 1;
1756 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001757 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001758 do {
1759 if (nextIndex == angleCount) {
1760 nextIndex = 0;
1761 }
1762 angle = sorted[nextIndex];
1763 Segment* segment = angle->segment();
1764 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001765 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001766 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001767 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001768 maxWinding = winding;
1769 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001770 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001771 }
1772 } while (++nextIndex != lastIndex);
1773 return windSum(SkMin32(startIndex, endIndex));
1774 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001775
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001776 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001777 int bestT = -1;
1778 SkScalar top = bounds().fTop;
1779 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001780 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001781 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001782 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001783 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001784 if (fTs[start].fWindValue == 0) {
1785 continue;
1786 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001787 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001788 double startT = fTs[start].fT;
1789 double endT = fTs[end].fT;
1790 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001791 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001792 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001793 // FIXME: always use original and limit results to T values within
1794 // start t and end t.
1795 // OPTIMIZE: use specialty function that intersects ray with curve,
1796 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001797 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1798 false, intersections);
1799 if (pts == 0) {
1800 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001801 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001802 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1803 // if the intersection is edge on, wait for another one
1804 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001805 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001806 for (int index = 0; index < pts; ++index) {
1807 SkPoint pt;
1808 double foundT = intersections.fT[0][index];
1809 double testT = startT + (endT - startT) * foundT;
1810 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1811 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001812 if (fVerb > SkPath::kLine_Verb
1813 && !approximately_less_than_zero(foundT)
1814 && !approximately_greater_than_one(foundT)) {
1815 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1816 if (approximately_zero(dx)) {
1817 continue;
1818 }
1819 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001820 bestY = pt.fY;
1821 bestT = foundT < 1 ? start : end;
1822 hitT = testT;
1823 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001824 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001825 } while (fTs[end].fT != 1);
1826 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001827 }
caryclark@google.com18063442012-07-25 12:05:18 +00001828
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001829 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1830 // if a segment is connected to this one, consider it crossing
1831 int tIndex;
1832 if (fPts[0].fX == basePt.fX) {
1833 tIndex = 0;
1834 do {
1835 const Span& sSpan = fTs[tIndex];
1836 const Segment* sOther = sSpan.fOther;
1837 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1838 continue;
1839 }
1840 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1841 : sOther->fBounds.fRight > basePt.fX) {
1842 return true;
1843 }
1844 } while (fTs[++tIndex].fT == 0);
1845 }
1846 if (fPts[fVerb].fX == basePt.fX) {
1847 tIndex = fTs.count() - 1;
1848 do {
1849 const Span& eSpan = fTs[tIndex];
1850 const Segment* eOther = eSpan.fOther;
1851 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1852 continue;
1853 }
1854 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1855 : eOther->fBounds.fRight > basePt.fX) {
1856 return true;
1857 }
1858 } while (fTs[--tIndex].fT == 1);
1859 }
1860 return false;
1861 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001862
caryclark@google.com18063442012-07-25 12:05:18 +00001863 bool decrementSpan(Span* span) {
1864 SkASSERT(span->fWindValue > 0);
1865 if (--(span->fWindValue) == 0) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001866 if (!span->fDone) {
1867 span->fDone = true;
1868 ++fDoneSpans;
1869 }
caryclark@google.com18063442012-07-25 12:05:18 +00001870 return true;
1871 }
1872 return false;
1873 }
1874
caryclark@google.com15fa1382012-05-07 20:49:36 +00001875 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001876 SkASSERT(fDoneSpans <= fTs.count());
1877 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001878 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001879
1880 bool done(int min) const {
1881 return fTs[min].fDone;
1882 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001883
caryclark@google.com47580692012-07-23 12:14:49 +00001884 bool done(const Angle& angle) const {
caryclark@google.comf839c032012-10-26 21:03:50 +00001885 return done(SkMin32(angle.start(), angle.end()));
caryclark@google.com47580692012-07-23 12:14:49 +00001886 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001887
caryclark@google.com235f56a2012-09-14 14:19:30 +00001888 Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00001889 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001890 bool& unsortable, ShapeOp op,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001891 const int aXorMask, const int bXorMask) {
1892 const int startIndex = nextStart;
1893 const int endIndex = nextEnd;
1894 int outerWinding = winding;
1895 int innerWinding = winding + spanWinding;
1896 #if DEBUG_WINDING
1897 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1898 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
1899 #endif
1900 if (useInnerWinding(outerWinding, innerWinding)) {
1901 outerWinding = innerWinding;
1902 }
1903 SkASSERT(startIndex != endIndex);
1904 int count = fTs.count();
1905 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1906 : startIndex > 0);
1907 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001908 #if PRECISE_T_SORT
1909 int end = nextExactSpan(startIndex, step);
1910 #else
caryclark@google.com235f56a2012-09-14 14:19:30 +00001911 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001912 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001913 SkASSERT(end >= 0);
1914 Span* endSpan = &fTs[end];
1915 Segment* other;
1916 if (isSimple(end)) {
1917 // mark the smaller of startIndex, endIndex done, and all adjacent
1918 // spans with the same T value (but not 'other' spans)
1919 #if DEBUG_WINDING
1920 SkDebugf("%s simple\n", __FUNCTION__);
1921 #endif
1922 markDone(SkMin32(startIndex, endIndex), outerWinding);
1923 other = endSpan->fOther;
1924 nextStart = endSpan->fOtherIndex;
1925 double startT = other->fTs[nextStart].fT;
1926 nextEnd = nextStart;
1927 do {
1928 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001929 }
1930 #if PRECISE_T_SORT
1931 while (precisely_zero(startT - other->fTs[nextEnd].fT));
1932 #else
1933 while (approximately_zero(startT - other->fTs[nextEnd].fT));
1934 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001935 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1936 return other;
1937 }
1938 // more than one viable candidate -- measure angles to find best
1939 SkTDArray<Angle> angles;
1940 SkASSERT(startIndex - endIndex != 0);
1941 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1942 addTwoAngles(startIndex, end, angles);
1943 buildAngles(end, angles);
1944 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001945 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001946 int angleCount = angles.count();
1947 int firstIndex = findStartingEdge(sorted, startIndex, end);
1948 SkASSERT(firstIndex >= 0);
1949 #if DEBUG_SORT
1950 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
1951 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001952 if (!sortable) {
1953 unsortable = true;
1954 return NULL;
1955 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001956 SkASSERT(sorted[firstIndex]->segment() == this);
1957 #if DEBUG_WINDING
1958 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
1959 #endif
1960 int aSumWinding = winding;
1961 int bSumWinding = winding;
1962 bool angleIsOp = sorted[firstIndex]->segment()->operand();
1963 int angleSpan = spanSign(sorted[firstIndex]);
1964 if (angleIsOp) {
1965 bSumWinding -= angleSpan;
1966 } else {
1967 aSumWinding -= angleSpan;
1968 }
1969 int nextIndex = firstIndex + 1;
1970 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1971 const Angle* foundAngle = NULL;
1972 // FIXME: found done logic probably fails if there are more than 4
1973 // sorted angles. It should bias towards the first and last undone
1974 // edges -- but not sure that it won't choose a middle (incorrect)
1975 // edge if one is undone
1976 bool foundDone = false;
1977 bool foundDone2 = false;
1978 // iterate through the angle, and compute everyone's winding
1979 bool altFlipped = false;
1980 bool foundFlipped = false;
1981 int foundMax = SK_MinS32;
1982 int foundSum = SK_MinS32;
1983 Segment* nextSegment;
1984 int lastNonZeroSum = winding;
1985 do {
1986 if (nextIndex == angleCount) {
1987 nextIndex = 0;
1988 }
1989 const Angle* nextAngle = sorted[nextIndex];
1990 nextSegment = nextAngle->segment();
caryclark@google.comf839c032012-10-26 21:03:50 +00001991 bool nextDone = nextSegment->done(*nextAngle);
1992 bool nextTiny = nextSegment->tiny(*nextAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001993 angleIsOp = nextSegment->operand();
1994 int sumWinding = angleIsOp ? bSumWinding : aSumWinding;
1995 int maxWinding = sumWinding;
1996 if (sumWinding) {
1997 lastNonZeroSum = sumWinding;
1998 }
1999 sumWinding -= nextSegment->spanSign(nextAngle);
2000 int xorMask = nextSegment->operand() ? bXorMask : aXorMask;
2001 bool otherNonZero;
2002 if (angleIsOp) {
2003 bSumWinding = sumWinding;
2004 otherNonZero = aSumWinding & aXorMask;
2005 } else {
2006 aSumWinding = sumWinding;
2007 otherNonZero = bSumWinding & bXorMask;
2008 }
2009 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002010 #if 0 && DEBUG_WINDING
caryclark@google.com235f56a2012-09-14 14:19:30 +00002011 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
2012 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2013 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
2014 #endif
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002015
caryclark@google.com235f56a2012-09-14 14:19:30 +00002016 if (!(sumWinding & xorMask) && activeOp(angleIsOp, otherNonZero, op)) {
2017 if (!active) {
2018 markDone(SkMin32(startIndex, endIndex), outerWinding);
2019 // FIXME: seems like a bug that this isn't calling userInnerWinding
2020 nextSegment->markWinding(SkMin32(nextAngle->start(),
2021 nextAngle->end()), maxWinding);
2022 #if DEBUG_WINDING
2023 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
2024 #endif
2025 return NULL;
2026 }
2027 if (!foundAngle || foundDone) {
2028 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002029 foundDone = nextDone && !nextTiny;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002030 foundFlipped = altFlipped;
2031 foundMax = maxWinding;
2032 }
2033 continue;
2034 }
2035 if (!(maxWinding & xorMask) && (!foundAngle || foundDone2)
2036 && activeOp(angleIsOp, otherNonZero, op)) {
2037 #if DEBUG_WINDING
2038 if (foundAngle && foundDone2) {
2039 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
2040 }
2041 #endif
2042 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002043 foundDone2 = nextDone && !nextTiny;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002044 foundFlipped = altFlipped;
2045 foundSum = sumWinding;
2046 }
2047 if (nextSegment->done()) {
2048 continue;
2049 }
2050 // if the winding is non-zero, nextAngle does not connect to
2051 // current chain. If we haven't done so already, mark the angle
2052 // as done, record the winding value, and mark connected unambiguous
2053 // segments as well.
2054 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
2055 if (useInnerWinding(maxWinding, sumWinding)) {
2056 maxWinding = sumWinding;
2057 }
2058 Span* last;
2059 if (foundAngle) {
2060 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
2061 } else {
2062 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
2063 }
2064 if (last) {
2065 *chase.append() = last;
2066 }
2067 }
2068 } while (++nextIndex != lastIndex);
2069 markDone(SkMin32(startIndex, endIndex), outerWinding);
2070 if (!foundAngle) {
2071 return NULL;
2072 }
2073 nextStart = foundAngle->start();
2074 nextEnd = foundAngle->end();
2075 nextSegment = foundAngle->segment();
2076 int flipped = foundFlipped ? -1 : 1;
2077 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2078 SkMin32(nextStart, nextEnd));
2079 if (winding) {
2080 #if DEBUG_WINDING
2081 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2082 if (foundSum == SK_MinS32) {
2083 SkDebugf("?");
2084 } else {
2085 SkDebugf("%d", foundSum);
2086 }
2087 SkDebugf(" foundMax=");
2088 if (foundMax == SK_MinS32) {
2089 SkDebugf("?");
2090 } else {
2091 SkDebugf("%d", foundMax);
2092 }
2093 SkDebugf("\n");
2094 #endif
2095 winding = foundSum;
2096 }
2097 #if DEBUG_WINDING
2098 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
2099 #endif
2100 return nextSegment;
2101 }
caryclark@google.com47580692012-07-23 12:14:49 +00002102
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002103 // so the span needs to contain the pairing info found here
2104 // this should include the winding computed for the edge, and
2105 // what edge it connects to, and whether it is discarded
2106 // (maybe discarded == abs(winding) > 1) ?
2107 // only need derivatives for duration of sorting, add a new struct
2108 // for pairings, remove extra spans that have zero length and
2109 // reference an unused other
2110 // for coincident, the last span on the other may be marked done
2111 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002112
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002113 // if loop is exhausted, contour may be closed.
2114 // FIXME: pass in close point so we can check for closure
2115
2116 // given a segment, and a sense of where 'inside' is, return the next
2117 // segment. If this segment has an intersection, or ends in multiple
2118 // segments, find the mate that continues the outside.
2119 // note that if there are multiples, but no coincidence, we can limit
2120 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002121
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002122 // mark found segments as done
2123
caryclark@google.com15fa1382012-05-07 20:49:36 +00002124 // start is the index of the beginning T of this edge
2125 // it is guaranteed to have an end which describes a non-zero length (?)
2126 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002127 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002128 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2129 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002130 const int startIndex = nextStart;
2131 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002132 int outerWinding = winding;
2133 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002134 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002135 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2136 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002137 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002138 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002139 outerWinding = innerWinding;
2140 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002141 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002142 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002143 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2144 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002145 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002146 #if PRECISE_T_SORT
2147 int end = nextExactSpan(startIndex, step);
2148 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002149 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002150 #endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002151 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002152 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002153 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002154 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002155 // mark the smaller of startIndex, endIndex done, and all adjacent
2156 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002157 #if DEBUG_WINDING
2158 SkDebugf("%s simple\n", __FUNCTION__);
2159 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002160 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002161 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002162 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002163 double startT = other->fTs[nextStart].fT;
2164 nextEnd = nextStart;
2165 do {
2166 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002167 }
2168 #if PRECISE_T_SORT
2169 while (precisely_zero(startT - other->fTs[nextEnd].fT));
2170 #else
2171 while (approximately_zero(startT - other->fTs[nextEnd].fT));
2172 #endif
caryclark@google.com495f8e42012-05-31 13:13:11 +00002173 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002174 return other;
2175 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002176 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002177 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002178 SkASSERT(startIndex - endIndex != 0);
2179 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002180 addTwoAngles(startIndex, end, angles);
2181 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002182 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002183 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002184 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002185 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002186 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002187 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002188 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com47580692012-07-23 12:14:49 +00002189 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002190 if (!sortable) {
2191 unsortable = true;
2192 return NULL;
2193 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002194 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002195 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002196 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002197 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002198 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002199 int nextIndex = firstIndex + 1;
2200 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2201 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002202 // FIXME: found done logic probably fails if there are more than 4
2203 // sorted angles. It should bias towards the first and last undone
2204 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002205 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002206 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002207 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002208 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002209 bool altFlipped = false;
2210 bool foundFlipped = false;
2211 int foundMax = SK_MinS32;
2212 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002213 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002214 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002215 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002216 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002217 nextIndex = 0;
2218 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002219 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002220 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002221 if (sumWinding) {
2222 lastNonZeroSum = sumWinding;
2223 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002224 nextSegment = nextAngle->segment();
caryclark@google.comf839c032012-10-26 21:03:50 +00002225 bool nextDone = nextSegment->done(*nextAngle);
2226 bool nextTiny = nextSegment->tiny(*nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002227 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002228 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002229 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002230 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002231 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2232 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002233 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002234 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002235 if (!active) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002236 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002237 // FIXME: seems like a bug that this isn't calling userInnerWinding
caryclark@google.com47580692012-07-23 12:14:49 +00002238 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002239 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002240 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002241 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002242 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002243 return NULL;
2244 }
caryclark@google.com47580692012-07-23 12:14:49 +00002245 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002246 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002247 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002248 foundFlipped = altFlipped;
2249 foundMax = maxWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002250 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002251 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002252 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002253
caryclark@google.com24bec792012-08-20 12:43:57 +00002254 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002255 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002256 if (foundAngle && foundDone2) {
2257 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002258 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002259 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002260 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002261 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002262 foundFlipped = altFlipped;
2263 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002264 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002265 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002266 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002267 }
2268 // if the winding is non-zero, nextAngle does not connect to
2269 // current chain. If we haven't done so already, mark the angle
2270 // as done, record the winding value, and mark connected unambiguous
2271 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002272 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002273 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002274 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002275 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002276 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002277 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002278 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002279 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002280 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002281 }
2282 if (last) {
2283 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002284 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002285 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002286 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002287 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002288 if (!foundAngle) {
2289 return NULL;
2290 }
2291 nextStart = foundAngle->start();
2292 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002293 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002294 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002295 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2296 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002297 if (winding) {
2298 #if DEBUG_WINDING
2299 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2300 if (foundSum == SK_MinS32) {
2301 SkDebugf("?");
2302 } else {
2303 SkDebugf("%d", foundSum);
2304 }
2305 SkDebugf(" foundMax=");
2306 if (foundMax == SK_MinS32) {
2307 SkDebugf("?");
2308 } else {
2309 SkDebugf("%d", foundMax);
2310 }
2311 SkDebugf("\n");
2312 #endif
2313 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002314 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002315 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002316 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002317 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002318 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002319 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002320
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002321 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002322 const int startIndex = nextStart;
2323 const int endIndex = nextEnd;
2324 SkASSERT(startIndex != endIndex);
2325 int count = fTs.count();
2326 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2327 : startIndex > 0);
2328 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002329 #if PRECISE_T_SORT
2330 int end = nextExactSpan(startIndex, step);
2331 #else
caryclark@google.com24bec792012-08-20 12:43:57 +00002332 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002333 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002334 SkASSERT(end >= 0);
2335 Span* endSpan = &fTs[end];
2336 Segment* other;
2337 markDone(SkMin32(startIndex, endIndex), 1);
2338 if (isSimple(end)) {
2339 #if DEBUG_WINDING
2340 SkDebugf("%s simple\n", __FUNCTION__);
2341 #endif
2342 other = endSpan->fOther;
2343 nextStart = endSpan->fOtherIndex;
2344 double startT = other->fTs[nextStart].fT;
2345 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002346 if ((approximately_less_than_zero(startT) && step < 0)
2347 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002348 step = -step;
2349 SkDEBUGCODE(firstLoop = false;)
2350 }
2351 do {
2352 nextEnd = nextStart;
2353 do {
2354 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002355 }
2356 #if PRECISE_T_SORT
2357 while (precisely_zero(startT - other->fTs[nextEnd].fT));
2358 #else
2359 while (approximately_zero(startT - other->fTs[nextEnd].fT));
2360 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002361 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2362 break;
2363 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002364 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002365 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002366 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002367 SkDEBUGCODE(firstLoop = false;)
2368 step = -step;
2369 } while (true);
2370 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2371 return other;
2372 }
2373 SkTDArray<Angle> angles;
2374 SkASSERT(startIndex - endIndex != 0);
2375 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2376 addTwoAngles(startIndex, end, angles);
2377 buildAngles(end, angles);
2378 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002379 bool sortable = SortAngles(angles, sorted);
caryclark@google.com24bec792012-08-20 12:43:57 +00002380 int angleCount = angles.count();
2381 int firstIndex = findStartingEdge(sorted, startIndex, end);
2382 SkASSERT(firstIndex >= 0);
2383 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002384 debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002385 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002386 if (!sortable) {
2387 unsortable = true;
2388 return NULL;
2389 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002390 SkASSERT(sorted[firstIndex]->segment() == this);
2391 int nextIndex = firstIndex + 1;
2392 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2393 const Angle* nextAngle;
2394 Segment* nextSegment;
2395 do {
2396 if (nextIndex == angleCount) {
2397 nextIndex = 0;
2398 }
2399 nextAngle = sorted[nextIndex];
2400 nextSegment = nextAngle->segment();
2401 if (!nextSegment->done(*nextAngle)) {
2402 break;
2403 }
2404 if (++nextIndex == lastIndex) {
2405 return NULL;
2406 }
2407 } while (true);
2408 nextStart = nextAngle->start();
2409 nextEnd = nextAngle->end();
2410 return nextSegment;
2411 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002412
2413 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2414 int angleCount = sorted.count();
2415 int firstIndex = -1;
2416 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2417 const Angle* angle = sorted[angleIndex];
2418 if (angle->segment() == this && angle->start() == end &&
2419 angle->end() == start) {
2420 firstIndex = angleIndex;
2421 break;
2422 }
2423 }
2424 return firstIndex;
2425 }
2426
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002427 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com235f56a2012-09-14 14:19:30 +00002428 void findTooCloseToCall(bool isXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002429 int count = fTs.count();
2430 if (count < 3) { // require t=0, x, 1 at minimum
2431 return;
2432 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002433 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002434 int moCount;
2435 Span* match;
2436 Segment* mOther;
2437 do {
2438 match = &fTs[matchIndex];
2439 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002440 // FIXME: allow quads, cubics to be near coincident?
2441 if (mOther->fVerb == SkPath::kLine_Verb) {
2442 moCount = mOther->fTs.count();
2443 if (moCount >= 3) {
2444 break;
2445 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002446 }
2447 if (++matchIndex >= count) {
2448 return;
2449 }
2450 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002451 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002452 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002453 // look for a pair of nearby T values that map to the same (x,y) value
2454 // if found, see if the pair of other segments share a common point. If
2455 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002456 for (int index = matchIndex + 1; index < count; ++index) {
2457 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002458 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002459 continue;
2460 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002461 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002462 if (tOther->fVerb != SkPath::kLine_Verb) {
2463 continue; // FIXME: allow quads, cubics to be near coincident?
2464 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002465 int toCount = tOther->fTs.count();
2466 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002467 continue;
2468 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002469 const SkPoint* testPt = &xyAtT(test);
2470 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002471 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002472 moCount = toCount;
2473 match = test;
2474 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002475 matchPt = testPt;
2476 continue;
2477 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002478 int moStart = -1;
2479 int moEnd = -1;
2480 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002481 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002482 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002483 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002484 continue;
2485 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002486 if (moSpan.fOther == this) {
2487 if (moSpan.fOtherT == match->fT) {
2488 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002489 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002490 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002491 continue;
2492 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002493 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002494 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2495 moStart = -1;
2496 break;
2497 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002498 SkASSERT(moEnd == -1);
2499 moEnd = moIndex;
2500 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002501 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002502 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002503 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002504 continue;
2505 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002506 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002507 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002508 continue;
2509 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002510 int toStart = -1;
2511 int toEnd = -1;
2512 double toStartT, toEndT;
2513 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2514 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002515 if (toSpan.fDone) {
2516 continue;
2517 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002518 if (toSpan.fOther == this) {
2519 if (toSpan.fOtherT == test->fT) {
2520 toStart = toIndex;
2521 toStartT = toSpan.fT;
2522 }
2523 continue;
2524 }
2525 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002526 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2527 moStart = -1;
2528 break;
2529 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002530 SkASSERT(toEnd == -1);
2531 toEnd = toIndex;
2532 toEndT = toSpan.fT;
2533 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002534 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002535 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2536 if (toStart <= 0 || toEnd <= 0) {
2537 continue;
2538 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002539 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002540 continue;
2541 }
2542 // test to see if the segment between there and here is linear
2543 if (!mOther->isLinear(moStart, moEnd)
2544 || !tOther->isLinear(toStart, toEnd)) {
2545 continue;
2546 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002547 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002548 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002549 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002550 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002551 // FIXME: this is bogus for multiple ops
2552 // the xorMask needs to be accumulated from the union of the two
2553 // edges -- which means that the segment must have its own copy of the mask
2554 mOther->addTCoincident(isXor, moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002555 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002556 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002557 }
2558
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002559 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002560 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002561 // a) mark spans with either end unsortable as done, or
2562 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2563 // when encountering an unsortable span
2564
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002565 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2566 // and use more concise logic like the old edge walker code?
2567 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002568 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002569 // iterate through T intersections and return topmost
2570 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002571 SkASSERT(!done());
2572 int firstT;
2573 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002574 SkPoint topPt;
2575 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002576 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002577 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002578 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002579 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002580 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002581 const Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00002582 if (span.fUnsortableStart | lastUnsortable) {
2583 goto next;
2584 }
2585 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002586 const SkPoint& intercept = xyAtT(&span);
2587 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2588 && topPt.fX > intercept.fX)) {
2589 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002590 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002591 } else if (topPt == intercept) {
2592 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002593 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002594 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002595 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002596 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002597 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002598 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002599 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002600 int step = 1;
2601 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002602 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002603 step = -1;
2604 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002605 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002606 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002607 // if the topmost T is not on end, or is three-way or more, find left
2608 // look for left-ness from tLeft to firstT (matching y of other)
2609 SkTDArray<Angle> angles;
2610 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002611 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002612 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002613 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002614 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002615 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00002616 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002617 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00002618 if (!sortable) {
2619 return NULL;
2620 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002621 // skip edges that have already been processed
2622 firstT = -1;
2623 Segment* leftSegment;
2624 do {
2625 const Angle* angle = sorted[++firstT];
caryclark@google.comf839c032012-10-26 21:03:50 +00002626 SkASSERT(!angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002627 leftSegment = angle->segment();
2628 tIndex = angle->end();
2629 endIndex = angle->start();
2630 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002631 return leftSegment;
2632 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002633
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002634 // FIXME: not crazy about this
2635 // when the intersections are performed, the other index is into an
2636 // incomplete array. as the array grows, the indices become incorrect
2637 // while the following fixes the indices up again, it isn't smart about
2638 // skipping segments whose indices are already correct
2639 // assuming we leave the code that wrote the index in the first place
2640 void fixOtherTIndex() {
2641 int iCount = fTs.count();
2642 for (int i = 0; i < iCount; ++i) {
2643 Span& iSpan = fTs[i];
2644 double oT = iSpan.fOtherT;
2645 Segment* other = iSpan.fOther;
2646 int oCount = other->fTs.count();
2647 for (int o = 0; o < oCount; ++o) {
2648 Span& oSpan = other->fTs[o];
2649 if (oT == oSpan.fT && this == oSpan.fOther) {
2650 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002651 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002652 }
2653 }
2654 }
2655 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002656
caryclark@google.com495f8e42012-05-31 13:13:11 +00002657 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002658 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002659 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002660 SkASSERT(end >= 0);
2661 if (multipleSpans(end)) {
2662 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002663 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002664 const Span& endSpan = fTs[end];
2665 Segment* other = endSpan.fOther;
2666 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002667 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002668 Span* last = other->innerChaseDone(index, step, winding);
2669 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002670 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002671 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002672
caryclark@google.com59823f72012-08-09 18:17:47 +00002673 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002674 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002675 SkASSERT(end >= 0);
2676 if (multipleSpans(end)) {
2677 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002678 }
2679 const Span& endSpan = fTs[end];
2680 Segment* other = endSpan.fOther;
2681 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002682 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002683 int min = SkMin32(index, otherEnd);
2684 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002685 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002686 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002687 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002688 Span* last = other->innerChaseWinding(index, step, winding);
2689 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002690 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002691 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002692
caryclark@google.com235f56a2012-09-14 14:19:30 +00002693 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2694 fDoneSpans = 0;
2695 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002696 fPts = pts;
2697 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002698 }
2699
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002700 bool intersected() const {
2701 return fTs.count() > 0;
2702 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002703
2704 bool isConnected(int startIndex, int endIndex) const {
2705 return fTs[startIndex].fWindSum != SK_MinS32
2706 || fTs[endIndex].fWindSum != SK_MinS32;
2707 }
2708
caryclark@google.com235f56a2012-09-14 14:19:30 +00002709 bool isHorizontal() const {
2710 return fBounds.fTop == fBounds.fBottom;
2711 }
2712
caryclark@google.com15fa1382012-05-07 20:49:36 +00002713 bool isLinear(int start, int end) const {
2714 if (fVerb == SkPath::kLine_Verb) {
2715 return true;
2716 }
2717 if (fVerb == SkPath::kQuad_Verb) {
2718 SkPoint qPart[3];
2719 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2720 return QuadIsLinear(qPart);
2721 } else {
2722 SkASSERT(fVerb == SkPath::kCubic_Verb);
2723 SkPoint cPart[4];
2724 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2725 return CubicIsLinear(cPart);
2726 }
2727 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002728
2729 // OPTIMIZE: successive calls could start were the last leaves off
2730 // or calls could specialize to walk forwards or backwards
2731 bool isMissing(double startT) const {
2732 size_t tCount = fTs.count();
2733 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002734 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002735 return false;
2736 }
2737 }
2738 return true;
2739 }
2740
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002741 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002742 int count = fTs.count();
2743 if (count == 2) {
2744 return true;
2745 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002746 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002747 if (approximately_less_than_zero(t)) {
2748 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002749 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002750 if (approximately_greater_than_one(t)) {
2751 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002752 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002753 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002754 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002755
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002756 bool isVertical() const {
2757 return fBounds.fLeft == fBounds.fRight;
2758 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002759
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002760 SkScalar leftMost(int start, int end) const {
2761 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2762 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002763
caryclark@google.com495f8e42012-05-31 13:13:11 +00002764 // this span is excluded by the winding rule -- chase the ends
2765 // as long as they are unambiguous to mark connections as done
2766 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002767 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002768 int index = angle->start();
2769 int endIndex = angle->end();
2770 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002771 Span* last = innerChaseDone(index, step, winding);
2772 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002773 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002774 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002775
caryclark@google.com59823f72012-08-09 18:17:47 +00002776 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002777 int index = angle->start();
2778 int endIndex = angle->end();
2779 int min = SkMin32(index, endIndex);
2780 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002781 Span* last = innerChaseWinding(index, step, winding);
2782 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002783 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002784 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002785
caryclark@google.com495f8e42012-05-31 13:13:11 +00002786 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002787 // This may be called when the segment is already marked done. While this
2788 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002789 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002790 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002791 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002792 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002793 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002794 double referenceT = fTs[index].fT;
2795 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002796 #if PRECISE_T_SORT
2797 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2798 markOneDone(__FUNCTION__, lesser, winding);
2799 }
2800 do {
2801 markOneDone(__FUNCTION__, index, winding);
2802 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2803 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002804 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002805 markOneDone(__FUNCTION__, lesser, winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002806 }
2807 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002808 markOneDone(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002809 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00002810 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002811 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002812
caryclark@google.com24bec792012-08-20 12:43:57 +00002813 void markOneDone(const char* funName, int tIndex, int winding) {
2814 Span* span = markOneWinding(funName, tIndex, winding);
2815 if (!span) {
2816 return;
2817 }
2818 span->fDone = true;
2819 fDoneSpans++;
2820 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002821
caryclark@google.com24bec792012-08-20 12:43:57 +00002822 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2823 Span& span = fTs[tIndex];
2824 if (span.fDone) {
2825 return NULL;
2826 }
2827 #if DEBUG_MARK_DONE
2828 debugShowNewWinding(funName, span, winding);
2829 #endif
2830 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002831 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002832 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002833 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002834 span.fWindSum = winding;
2835 return &span;
2836 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002837
caryclark@google.comf839c032012-10-26 21:03:50 +00002838 // note that just because a span has one end that is unsortable, that's
2839 // not enough to mark it done. The other end may be sortable, allowing the
2840 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002841 void markUnsortable(int start, int end) {
2842 Span* span = &fTs[start];
2843 if (start < end) {
2844 span->fUnsortableStart = true;
2845 } else {
2846 --span;
2847 span->fUnsortableEnd = true;
2848 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002849 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002850 return;
2851 }
2852 span->fDone = true;
2853 fDoneSpans++;
2854 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002855
caryclark@google.com59823f72012-08-09 18:17:47 +00002856 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002857 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002858 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002859 double referenceT = fTs[index].fT;
2860 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002861 #if PRECISE_T_SORT
2862 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2863 markOneWinding(__FUNCTION__, lesser, winding);
2864 }
2865 do {
2866 markOneWinding(__FUNCTION__, index, winding);
2867 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2868 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002869 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002870 markOneWinding(__FUNCTION__, lesser, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002871 }
2872 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002873 markOneWinding(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002874 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00002875 #endif
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002876 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002877
caryclark@google.com2ddff932012-08-07 21:25:27 +00002878 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002879 int nextDoorWind = SK_MaxS32;
2880 if (tIndex > 0) {
2881 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002882 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002883 nextDoorWind = below.fWindValue;
2884 }
2885 }
2886 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2887 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002888 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002889 nextDoorWind = above.fWindValue;
2890 }
2891 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002892 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2893 const Span& below = fTs[tIndex - 1];
2894 nextDoorWind = below.fWindValue;
2895 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002896 if (nextDoorWind != SK_MaxS32) {
2897 Span& newSpan = fTs[tIndex];
2898 newSpan.fWindValue = nextDoorWind;
caryclark@google.comf839c032012-10-26 21:03:50 +00002899 if (!nextDoorWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002900 newSpan.fDone = true;
2901 ++fDoneSpans;
2902 }
2903 }
2904 }
2905
caryclark@google.com9764cc62012-07-12 19:29:45 +00002906 // return span if when chasing, two or more radiating spans are not done
2907 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2908 // candidate and the remaining spans have windValue == 0 (canceled by
2909 // coincidence). The coincident edges could either be removed altogether,
2910 // or this code could be more complicated in detecting this case. Worth it?
2911 bool multipleSpans(int end) const {
2912 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002913 }
2914
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002915 // This has callers for two different situations: one establishes the end
2916 // of the current span, and one establishes the beginning of the next span
2917 // (thus the name). When this is looking for the end of the current span,
2918 // coincidence is found when the beginning Ts contain -step and the end
2919 // contains step. When it is looking for the beginning of the next, the
2920 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002921 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002922 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002923 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002924 int count = fTs.count();
2925 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002926 while (step > 0 ? ++to < count : --to >= 0) {
2927 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002928 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002929 continue;
2930 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002931 return to;
2932 }
2933 return -1;
2934 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00002935
caryclark@google.coma461ff02012-10-11 12:54:23 +00002936#if PRECISE_T_SORT
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002937 // FIXME
2938 // this returns at any difference in T, vs. a preset minimum. It may be
2939 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002940 // OPTIMIZATION splitting this into separate loops for up/down steps
2941 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002942 int nextExactSpan(int from, int step) const {
2943 const Span& fromSpan = fTs[from];
2944 int count = fTs.count();
2945 int to = from;
2946 while (step > 0 ? ++to < count : --to >= 0) {
2947 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00002948 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002949 continue;
2950 }
2951 return to;
2952 }
2953 return -1;
2954 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002955#endif
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002956
caryclark@google.com235f56a2012-09-14 14:19:30 +00002957 bool operand() const {
2958 return fOperand;
2959 }
2960
2961 int oppSign(int startIndex, int endIndex) const {
2962 int result = startIndex < endIndex ? -fTs[startIndex].fWindValueOpp :
2963 fTs[endIndex].fWindValueOpp;
2964#if DEBUG_WIND_BUMP
2965 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2966#endif
2967 return result;
2968 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002969
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002970 const SkPoint* pts() const {
2971 return fPts;
2972 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002973
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002974 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002975 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002976 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2977 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002978 }
2979
caryclark@google.comf839c032012-10-26 21:03:50 +00002980 // This marks all spans unsortable so that this info is available for early
2981 // exclusion in find top and others. This could be optimized to only mark
2982 // adjacent spans that unsortable. However, this makes it difficult to later
2983 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002984 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00002985 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002986 int angleCount = angles.count();
2987 int angleIndex;
2988 angleList.setReserve(angleCount);
2989 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002990 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00002991 *angleList.append() = &angle;
2992 sortable &= !angle.unsortable();
2993 }
2994 if (sortable) {
2995 QSort<Angle>(angleList.begin(), angleList.end() - 1);
2996 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2997 if (angles[angleIndex].unsortable()) {
2998 sortable = false;
2999 break;
3000 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003001 }
3002 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003003 if (!sortable) {
3004 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3005 Angle& angle = angles[angleIndex];
3006 angle.segment()->markUnsortable(angle.start(), angle.end());
3007 }
3008 }
3009 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003010 }
3011
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003012 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003013 const Span& span(int tIndex) const {
3014 return fTs[tIndex];
3015 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003016
caryclark@google.com235f56a2012-09-14 14:19:30 +00003017 int spanSign(const Angle* angle) const {
3018 SkASSERT(angle->segment() == this);
3019 return spanSign(angle->start(), angle->end());
3020 }
3021
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003022 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003023 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue :
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003024 fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003025#if DEBUG_WIND_BUMP
3026 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3027#endif
3028 return result;
3029 }
3030
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003031 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003032 double t(int tIndex) const {
3033 return fTs[tIndex].fT;
3034 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003035
caryclark@google.comf839c032012-10-26 21:03:50 +00003036 bool tiny(const Angle& angle) const {
3037 int start = angle.start();
3038 int end = angle.end();
3039 const Span& mSpan = fTs[SkMin32(start, end)];
3040 return mSpan.fTiny;
3041 }
3042
caryclark@google.com18063442012-07-25 12:05:18 +00003043 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3044 double start) {
3045 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003046 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003047 *outsideTs.append() = end;
3048 *outsideTs.append() = start;
3049 }
3050 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003051
caryclark@google.com24bec792012-08-20 12:43:57 +00003052 void undoneSpan(int& start, int& end) {
3053 size_t tCount = fTs.count();
3054 size_t index;
3055 for (index = 0; index < tCount; ++index) {
3056 if (!fTs[index].fDone) {
3057 break;
3058 }
3059 }
3060 SkASSERT(index < tCount - 1);
3061 start = index;
3062 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003063 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003064 SkASSERT(index < tCount);
3065 SkASSERT(index < tCount);
3066 end = index;
3067 }
caryclark@google.com18063442012-07-25 12:05:18 +00003068
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003069 bool unsortable(int index) const {
3070 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3071 }
3072
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003073 void updatePts(const SkPoint pts[]) {
3074 fPts = pts;
3075 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003076
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003077 SkPath::Verb verb() const {
3078 return fVerb;
3079 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003080
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003081 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003082 return fTs[tIndex].fWindSum;
3083 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003084
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003085 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003086 int start = angle->start();
3087 int end = angle->end();
3088 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003089 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003090 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003091
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003092 int windValue(int tIndex) const {
3093 return fTs[tIndex].fWindValue;
3094 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003095
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003096 int windValue(const Angle* angle) const {
3097 int start = angle->start();
3098 int end = angle->end();
3099 int index = SkMin32(start, end);
3100 return windValue(index);
3101 }
3102
3103 SkScalar xAtT(const Span* span) const {
3104 return xyAtT(span).fX;
3105 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003106
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003107 const SkPoint& xyAtT(int index) const {
3108 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003109 }
3110
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003111 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003112 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003113 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003114 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003115 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003116 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003117 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003118 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003119 }
3120 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003121 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003122 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003123
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003124 SkScalar yAtT(int index) const {
3125 return yAtT(&fTs[index]);
3126 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003127
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003128 SkScalar yAtT(const Span* span) const {
3129 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003130 }
3131
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003132#if DEBUG_DUMP
3133 void dump() const {
3134 const char className[] = "Segment";
3135 const int tab = 4;
3136 for (int i = 0; i < fTs.count(); ++i) {
3137 SkPoint out;
3138 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3139 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003140 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003141 tab + sizeof(className), className, fID,
3142 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003143 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003144 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003145 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003146 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003147 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003148 }
3149#endif
3150
caryclark@google.com47580692012-07-23 12:14:49 +00003151#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003152 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003153 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003154 for (int i = 0; i < fTs.count(); ++i) {
3155 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3156 return;
3157 }
3158 }
3159 SkASSERT(0);
3160 }
3161#endif
3162
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003163#if DEBUG_DUMP
3164 int debugID() const {
3165 return fID;
3166 }
3167#endif
3168
caryclark@google.com24bec792012-08-20 12:43:57 +00003169#if DEBUG_WINDING
3170 void debugShowSums() const {
3171 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3172 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3173 for (int i = 0; i < fTs.count(); ++i) {
3174 const Span& span = fTs[i];
3175 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3176 if (span.fWindSum == SK_MinS32) {
3177 SkDebugf("?");
3178 } else {
3179 SkDebugf("%d", span.fWindSum);
3180 }
3181 SkDebugf("]");
3182 }
3183 SkDebugf("\n");
3184 }
3185#endif
3186
caryclark@google.comcc905052012-07-25 20:59:42 +00003187#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003188 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003189 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00003190 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00003191 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00003192 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
3193 }
3194 SkDebugf("\n");
3195 }
3196#endif
3197
caryclark@google.com027de222012-07-12 12:52:50 +00003198#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003199 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003200 if (done()) {
3201 return;
3202 }
3203 for (int i = 0; i < fTs.count(); ++i) {
3204 if (fTs[i].fDone) {
3205 continue;
3206 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003207 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003208 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3209 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3210 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3211 }
3212 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003213 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003214 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003215 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003216 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3217 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3218 if (fTs[i].fWindSum == SK_MinS32) {
3219 SkDebugf("?");
3220 } else {
3221 SkDebugf("%d", fTs[i].fWindSum);
3222 }
3223 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003224 }
3225 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003226
3227 // This isn't useful yet -- but leaving it in for now in case i think of something
3228 // to use it for
3229 void validateActiveSpans() const {
3230 if (done()) {
3231 return;
3232 }
3233 int tCount = fTs.count();
3234 for (int index = 0; index < tCount; ++index) {
3235 if (fTs[index].fDone) {
3236 continue;
3237 }
3238 // count number of connections which are not done
3239 int first = index;
3240 double baseT = fTs[index].fT;
3241 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3242 --first;
3243 }
3244 int last = index;
3245 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3246 ++last;
3247 }
3248 int connections = 0;
3249 connections += first > 0 && !fTs[first - 1].fDone;
3250 for (int test = first; test <= last; ++test) {
3251 connections += !fTs[test].fDone;
3252 const Segment* other = fTs[test].fOther;
3253 int oIndex = fTs[test].fOtherIndex;
3254 connections += !other->fTs[oIndex].fDone;
3255 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3256 }
3257 // SkASSERT(!(connections & 1));
3258 }
3259 }
caryclark@google.com027de222012-07-12 12:52:50 +00003260#endif
3261
caryclark@google.com0c803d02012-08-06 11:15:47 +00003262#if DEBUG_MARK_DONE
3263 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3264 const SkPoint& pt = xyAtT(&span);
3265 SkDebugf("%s id=%d", fun, fID);
3266 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3267 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3268 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3269 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003270 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3271 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3272 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3273 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003274 if (span.fWindSum == SK_MinS32) {
3275 SkDebugf("?");
3276 } else {
3277 SkDebugf("%d", span.fWindSum);
3278 }
3279 SkDebugf(" windValue=%d\n", span.fWindValue);
3280 }
3281#endif
3282
caryclark@google.com47580692012-07-23 12:14:49 +00003283#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003284 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003285 const int contourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003286 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003287 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003288 int lastSum = contourWinding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003289 int windSum = lastSum - spanSign(angles[first]);
caryclark@google.com03f97062012-08-21 13:13:52 +00003290 SkDebugf("%s %s contourWinding=%d sign=%d\n", fun, __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +00003291 contourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003292 int index = first;
3293 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003294 do {
3295 const Angle& angle = *angles[index];
3296 const Segment& segment = *angle.segment();
3297 int start = angle.start();
3298 int end = angle.end();
3299 const Span& sSpan = segment.fTs[start];
3300 const Span& eSpan = segment.fTs[end];
3301 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003302 if (!firstTime) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003303 lastSum = windSum;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003304 windSum -= segment.spanSign(&angle);
caryclark@google.comafe56de2012-07-24 18:11:03 +00003305 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003306 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 +00003307 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003308 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003309 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003310 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3311 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3312 mSpan.fWindValue);
3313 if (mSpan.fWindSum == SK_MinS32) {
3314 SkDebugf("?");
3315 } else {
3316 SkDebugf("%d", mSpan.fWindSum);
3317 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003318 SkDebugf(" winding: %d->%d (max=%d) done=%d tiny=%d\n", lastSum, windSum,
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003319 useInnerWinding(lastSum, windSum) ? windSum : lastSum,
caryclark@google.comf839c032012-10-26 21:03:50 +00003320 mSpan.fDone, mSpan.fTiny);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003321#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003322 angle.debugShow(segment.xyAtT(&sSpan));
3323#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003324 ++index;
3325 if (index == angles.count()) {
3326 index = 0;
3327 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003328 if (firstTime) {
3329 firstTime = false;
3330 }
caryclark@google.com47580692012-07-23 12:14:49 +00003331 } while (index != first);
3332 }
3333#endif
3334
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003335#if DEBUG_WINDING
3336 bool debugVerifyWinding(int start, int end, int winding) const {
3337 const Span& span = fTs[SkMin32(start, end)];
3338 int spanWinding = span.fWindSum;
3339 if (spanWinding == SK_MinS32) {
3340 return true;
3341 }
3342 int spanSign = SkSign32(start - end);
3343 int signedVal = spanSign * span.fWindValue;
3344 if (signedVal < 0) {
3345 spanWinding -= signedVal;
3346 }
3347 return span.fWindSum == winding;
3348 }
3349#endif
3350
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003351private:
3352 const SkPoint* fPts;
3353 SkPath::Verb fVerb;
3354 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003355 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00003356 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00003357 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003358#if DEBUG_DUMP
3359 int fID;
3360#endif
3361};
3362
caryclark@google.comb9738012012-07-03 19:53:30 +00003363class Contour;
3364
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003365struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003366 Contour* fContours[2];
3367 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003368 double fTs[2][2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00003369 bool fXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003370};
3371
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003372class Contour {
3373public:
3374 Contour() {
3375 reset();
3376#if DEBUG_DUMP
3377 fID = ++gContourID;
3378#endif
3379 }
3380
3381 bool operator<(const Contour& rh) const {
3382 return fBounds.fTop == rh.fBounds.fTop
3383 ? fBounds.fLeft < rh.fBounds.fLeft
3384 : fBounds.fTop < rh.fBounds.fTop;
3385 }
3386
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003387 void addCoincident(int index, Contour* other, int otherIndex,
3388 const Intersections& ts, bool swap) {
3389 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00003390 coincidence.fContours[0] = this;
3391 coincidence.fContours[1] = other;
3392 coincidence.fSegments[0] = index;
3393 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003394 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3395 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3396 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3397 coincidence.fTs[swap][0] = ts.fT[0][0];
3398 coincidence.fTs[swap][1] = ts.fT[0][1];
3399 coincidence.fTs[!swap][0] = ts.fT[1][0];
3400 coincidence.fTs[!swap][1] = ts.fT[1][1];
3401 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3402 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3403 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3404 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3405 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3406 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3407 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003408 coincidence.fXor = fOperand == other->fOperand ? fXor : true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003409 }
3410
3411 void addCross(const Contour* crosser) {
3412#ifdef DEBUG_CROSS
3413 for (int index = 0; index < fCrosses.count(); ++index) {
3414 SkASSERT(fCrosses[index] != crosser);
3415 }
3416#endif
3417 *fCrosses.append() = crosser;
3418 }
3419
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003420 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003421 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003422 fContainsCurves = true;
3423 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003424
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003425 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003426 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003427 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003428 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003429
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003430 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3431 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3432 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003433
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003434 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003435 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003436 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003437 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003438 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003439
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003440 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3441 containsIntercepts();
3442 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3443 }
3444
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003445 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003446 return fBounds;
3447 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003448
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003449 void collapseTriangles() {
3450 int segmentCount = fSegments.count();
3451 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3452 fSegments[sIndex].collapseTriangles(fXor);
3453 }
3454 }
3455
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003456 void complete() {
3457 setBounds();
3458 fContainsIntercepts = false;
3459 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003460
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003461 void containsIntercepts() {
3462 fContainsIntercepts = true;
3463 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003464
rmistry@google.comd6176b02012-08-23 18:14:13 +00003465 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003466 int &tIndex, double& hitT) {
3467 int segmentCount = fSegments.count();
3468 const Segment* bestSegment = NULL;
3469 for (int test = 0; test < segmentCount; ++test) {
3470 Segment* testSegment = &fSegments[test];
3471 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003472 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003473 continue;
3474 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003475 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003476 continue;
3477 }
3478 if (bounds.fLeft > basePt.fX) {
3479 continue;
3480 }
3481 if (bounds.fRight < basePt.fX) {
3482 continue;
3483 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003484 if (bounds.fLeft == bounds.fRight) {
3485 continue;
3486 }
3487 #if 0
3488 bool leftHalf = bounds.fLeft == basePt.fX;
3489 bool rightHalf = bounds.fRight == basePt.fX;
3490 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3491 basePt, leftHalf, rightHalf)) {
3492 continue;
3493 }
3494 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003495 double testHitT;
3496 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3497 if (testT >= 0) {
3498 bestSegment = testSegment;
3499 tIndex = testT;
3500 hitT = testHitT;
3501 }
3502 }
3503 return bestSegment;
3504 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003505
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003506 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003507 for (int index = 0; index < fCrosses.count(); ++index) {
3508 if (fCrosses[index] == crosser) {
3509 return true;
3510 }
3511 }
3512 return false;
3513 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003514
3515 const SkPoint& end() const {
3516 const Segment& segment = fSegments.back();
3517 return segment.pts()[segment.verb()];
3518 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003519
caryclark@google.com235f56a2012-09-14 14:19:30 +00003520 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003521 int segmentCount = fSegments.count();
3522 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003523 fSegments[sIndex].findTooCloseToCall(fXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003524 }
3525 }
3526
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003527 void fixOtherTIndex() {
3528 int segmentCount = fSegments.count();
3529 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3530 fSegments[sIndex].fixOtherTIndex();
3531 }
3532 }
3533
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003534 void reset() {
3535 fSegments.reset();
3536 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003537 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003538 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003539
caryclark@google.com235f56a2012-09-14 14:19:30 +00003540 // FIXME: for binary ops, need to keep both ops winding contributions separately
3541 // in edge array
3542 void resolveCoincidence() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003543 int count = fCoincidences.count();
3544 for (int index = 0; index < count; ++index) {
3545 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003546 Contour* thisContour = coincidence.fContours[0];
3547 Contour* otherContour = coincidence.fContours[1];
3548 int thisIndex = coincidence.fSegments[0];
3549 int otherIndex = coincidence.fSegments[1];
3550 Segment& thisOne = thisContour->fSegments[thisIndex];
3551 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003552 #if DEBUG_CONCIDENT
3553 thisOne.debugShowTs();
3554 other.debugShowTs();
3555 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003556 double startT = coincidence.fTs[0][0];
3557 double endT = coincidence.fTs[0][1];
caryclark@google.com32546db2012-08-31 20:55:07 +00003558 bool opposite = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003559 if (startT > endT) {
3560 SkTSwap<double>(startT, endT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003561 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003562 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003563 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003564 double oStartT = coincidence.fTs[1][0];
3565 double oEndT = coincidence.fTs[1][1];
3566 if (oStartT > oEndT) {
3567 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003568 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003569 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003570 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com32546db2012-08-31 20:55:07 +00003571 if (opposite) {
caryclark@google.comb9738012012-07-03 19:53:30 +00003572 // make sure startT and endT have t entries
caryclark@google.com32546db2012-08-31 20:55:07 +00003573 SkASSERT(opposite);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003574 if (startT > 0 || oEndT < 1
3575 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3576 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003577 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003578 if (oStartT > 0 || endT < 1
3579 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3580 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003581 }
3582 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003583 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003584 if (startT > 0 || oStartT > 0
3585 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003586 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003587 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003588 if (endT < 1 || oEndT < 1
3589 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003590 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003591 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003592 thisOne.addTCoincident(coincidence.fXor, startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003593 }
caryclark@google.com47580692012-07-23 12:14:49 +00003594 #if DEBUG_CONCIDENT
3595 thisOne.debugShowTs();
3596 other.debugShowTs();
3597 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003598 }
3599 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003600
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003601 const SkTArray<Segment>& segments() {
3602 return fSegments;
3603 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003604
caryclark@google.com235f56a2012-09-14 14:19:30 +00003605 void setOperand(bool isOp) {
3606 fOperand = isOp;
3607 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003608
caryclark@google.com235f56a2012-09-14 14:19:30 +00003609 void setXor(bool isXor) {
3610 fXor = isXor;
3611 }
3612
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003613#if !SORTABLE_CONTOURS
3614 void sortSegments() {
3615 int segmentCount = fSegments.count();
3616 fSortedSegments.setReserve(segmentCount);
3617 for (int test = 0; test < segmentCount; ++test) {
3618 *fSortedSegments.append() = &fSegments[test];
3619 }
3620 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
3621 fFirstSorted = 0;
3622 }
3623#endif
3624
caryclark@google.comf839c032012-10-26 21:03:50 +00003625 const SkPoint& start() const {
3626 return fSegments.front().pts()[0];
3627 }
3628
3629 void toPath(PathWrapper& path) const {
3630 int segmentCount = fSegments.count();
3631 const SkPoint& pt = fSegments.front().pts()[0];
3632 path.deferredMove(pt);
3633 for (int test = 0; test < segmentCount; ++test) {
3634 fSegments[test].addCurveTo(0, 1, path, true);
3635 }
3636 path.close();
3637 }
3638
3639 void toPartialBackward(PathWrapper& path) const {
3640 int segmentCount = fSegments.count();
3641 for (int test = segmentCount - 1; test >= 0; --test) {
3642 fSegments[test].addCurveTo(1, 0, path, true);
3643 }
3644 }
3645
3646 void toPartialForward(PathWrapper& path) const {
3647 int segmentCount = fSegments.count();
3648 for (int test = 0; test < segmentCount; ++test) {
3649 fSegments[test].addCurveTo(0, 1, path, true);
3650 }
3651 }
3652
3653#if 0 // FIXME: obsolete, remove
caryclark@google.com15fa1382012-05-07 20:49:36 +00003654 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3655 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003656 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003657 // segments' top, and not a true sort, so it could be ameniable to regular
3658 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003659 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003660 int segmentCount = fSegments.count();
3661 SkASSERT(segmentCount > 0);
3662 int best = -1;
3663 Segment* bestSegment = NULL;
3664 while (++best < segmentCount) {
3665 Segment* testSegment = &fSegments[best];
3666 if (testSegment->done()) {
3667 continue;
3668 }
3669 bestSegment = testSegment;
3670 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003671 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003672 if (!bestSegment) {
3673 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003674 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003675 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003676 for (int test = best + 1; test < segmentCount; ++test) {
3677 Segment* testSegment = &fSegments[test];
3678 if (testSegment->done()) {
3679 continue;
3680 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003681 if (testSegment->bounds().fTop > bestTop) {
3682 continue;
3683 }
3684 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003685 if (bestTop > testTop) {
3686 bestTop = testTop;
3687 bestSegment = testSegment;
3688 }
3689 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003690 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003691 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003692 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003693#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003694
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003695#if !SORTABLE_CONTOURS
caryclark@google.comf839c032012-10-26 21:03:50 +00003696 Segment* topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003697 int segmentCount = fSortedSegments.count();
3698 SkASSERT(segmentCount > 0);
3699 Segment* bestSegment = NULL;
caryclark@google.comf839c032012-10-26 21:03:50 +00003700 int sortedIndex = fFirstSorted;
3701 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
3702 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003703 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003704 if (sortedIndex == fFirstSorted) {
3705 ++fFirstSorted;
3706 }
3707 continue;
3708 }
3709 SkPoint testXY;
3710 testSegment->activeLeftTop(testXY);
3711 if (testXY.fY < topLeft.fY) {
3712 continue;
3713 }
3714 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
3715 continue;
3716 }
3717 if (bestXY.fY < testXY.fY) {
3718 continue;
3719 }
3720 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003721 continue;
3722 }
3723 bestSegment = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00003724 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003725 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003726 return bestSegment;
3727 }
3728#endif
3729
caryclark@google.com24bec792012-08-20 12:43:57 +00003730 Segment* undoneSegment(int& start, int& end) {
3731 int segmentCount = fSegments.count();
3732 for (int test = 0; test < segmentCount; ++test) {
3733 Segment* testSegment = &fSegments[test];
3734 if (testSegment->done()) {
3735 continue;
3736 }
3737 testSegment->undoneSpan(start, end);
3738 return testSegment;
3739 }
3740 return NULL;
3741 }
3742
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003743 int updateSegment(int index, const SkPoint* pts) {
3744 Segment& segment = fSegments[index];
3745 segment.updatePts(pts);
3746 return segment.verb() + 1;
3747 }
3748
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003749#if DEBUG_TEST
3750 SkTArray<Segment>& debugSegments() {
3751 return fSegments;
3752 }
3753#endif
3754
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003755#if DEBUG_DUMP
3756 void dump() {
3757 int i;
3758 const char className[] = "Contour";
3759 const int tab = 4;
3760 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
3761 for (i = 0; i < fSegments.count(); ++i) {
3762 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
3763 className, i);
3764 fSegments[i].dump();
3765 }
3766 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
3767 tab + sizeof(className), className,
3768 fBounds.fLeft, fBounds.fTop,
3769 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003770 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
3771 className, fContainsIntercepts);
3772 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
3773 className, fContainsCurves);
3774 }
3775#endif
3776
caryclark@google.com027de222012-07-12 12:52:50 +00003777#if DEBUG_ACTIVE_SPANS
3778 void debugShowActiveSpans() {
3779 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003780 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00003781 }
3782 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003783
3784 void validateActiveSpans() {
3785 for (int index = 0; index < fSegments.count(); ++index) {
3786 fSegments[index].validateActiveSpans();
3787 }
3788 }
caryclark@google.com027de222012-07-12 12:52:50 +00003789#endif
3790
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003791protected:
3792 void setBounds() {
3793 int count = fSegments.count();
3794 if (count == 0) {
3795 SkDebugf("%s empty contour\n", __FUNCTION__);
3796 SkASSERT(0);
3797 // FIXME: delete empty contour?
3798 return;
3799 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003800 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003801 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003802 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003803 }
3804 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003805
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003806private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003807 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003808#if !SORTABLE_CONTOURS
3809 SkTDArray<Segment*> fSortedSegments;
3810 int fFirstSorted;
3811#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003812 SkTDArray<Coincidence> fCoincidences;
3813 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003814 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003815 bool fContainsIntercepts;
3816 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003817 bool fOperand; // true for the second argument to a binary operator
3818 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003819#if DEBUG_DUMP
3820 int fID;
3821#endif
3822};
3823
3824class EdgeBuilder {
3825public:
3826
caryclark@google.comf839c032012-10-26 21:03:50 +00003827EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
3828 : fPath(path.nativePath())
3829 , fContours(contours)
3830{
3831 init();
3832}
3833
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003834EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003835 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003836 , fContours(contours)
3837{
caryclark@google.comf839c032012-10-26 21:03:50 +00003838 init();
3839}
3840
3841void init() {
3842 fCurrentContour = NULL;
3843 fOperand = false;
3844 fXorMask = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003845#if DEBUG_DUMP
3846 gContourID = 0;
3847 gSegmentID = 0;
3848#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00003849 fSecondHalf = preFetch();
3850}
3851
3852void addOperand(const SkPath& path) {
3853 fPath = &path;
caryclark@google.comf839c032012-10-26 21:03:50 +00003854 fXorMask = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003855 preFetch();
3856}
3857
3858void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003859 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003860 complete();
3861 if (fCurrentContour && !fCurrentContour->segments().count()) {
3862 fContours.pop_back();
3863 }
3864 // correct pointers in contours since fReducePts may have moved as it grew
3865 int cIndex = 0;
3866 int extraCount = fExtra.count();
3867 SkASSERT(extraCount == 0 || fExtra[0] == -1);
3868 int eIndex = 0;
3869 int rIndex = 0;
3870 while (++eIndex < extraCount) {
3871 int offset = fExtra[eIndex];
3872 if (offset < 0) {
3873 ++cIndex;
3874 continue;
3875 }
3876 fCurrentContour = &fContours[cIndex];
3877 rIndex += fCurrentContour->updateSegment(offset - 1,
3878 &fReducePts[rIndex]);
3879 }
3880 fExtra.reset(); // we're done with this
3881}
3882
3883ShapeOpMask xorMask() const {
3884 return fXorMask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003885}
3886
3887protected:
3888
3889void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003890 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003891 fCurrentContour->complete();
3892 fCurrentContour = NULL;
3893 }
3894}
3895
caryclark@google.com235f56a2012-09-14 14:19:30 +00003896// FIXME:remove once we can access path pts directly
3897int preFetch() {
3898 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003899 SkPoint pts[4];
3900 SkPath::Verb verb;
3901 do {
3902 verb = iter.next(pts);
3903 *fPathVerbs.append() = verb;
3904 if (verb == SkPath::kMove_Verb) {
3905 *fPathPts.append() = pts[0];
3906 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
3907 fPathPts.append(verb, &pts[1]);
3908 }
3909 } while (verb != SkPath::kDone_Verb);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003910 return fPathVerbs.count();
3911}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003912
caryclark@google.com235f56a2012-09-14 14:19:30 +00003913void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003914 SkPath::Verb reducedVerb;
3915 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003916 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003917 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003918 const SkPoint* finalCurveStart = NULL;
3919 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003920 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003921 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
3922 switch (verb) {
3923 case SkPath::kMove_Verb:
3924 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003925 if (!fCurrentContour) {
3926 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003927 fCurrentContour->setOperand(fOperand);
3928 fCurrentContour->setXor(fXorMask == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003929 *fExtra.append() = -1; // start new contour
3930 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003931 finalCurveEnd = pointsPtr++;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003932 continue;
3933 case SkPath::kLine_Verb:
3934 // skip degenerate points
3935 if (pointsPtr[-1].fX != pointsPtr[0].fX
3936 || pointsPtr[-1].fY != pointsPtr[0].fY) {
3937 fCurrentContour->addLine(&pointsPtr[-1]);
3938 }
3939 break;
3940 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00003941
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003942 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
3943 if (reducedVerb == 0) {
3944 break; // skip degenerate points
3945 }
3946 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00003947 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003948 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003949 break;
3950 }
3951 fCurrentContour->addQuad(&pointsPtr[-1]);
3952 break;
3953 case SkPath::kCubic_Verb:
3954 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
3955 if (reducedVerb == 0) {
3956 break; // skip degenerate points
3957 }
3958 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003959 *fExtra.append() =
3960 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003961 break;
3962 }
3963 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003964 *fExtra.append() =
3965 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003966 break;
3967 }
3968 fCurrentContour->addCubic(&pointsPtr[-1]);
3969 break;
3970 case SkPath::kClose_Verb:
3971 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003972 if (finalCurveStart && finalCurveEnd
3973 && *finalCurveStart != *finalCurveEnd) {
3974 *fReducePts.append() = *finalCurveStart;
3975 *fReducePts.append() = *finalCurveEnd;
3976 *fExtra.append() =
3977 fCurrentContour->addLine(fReducePts.end() - 2);
3978 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003979 complete();
3980 continue;
3981 default:
3982 SkDEBUGFAIL("bad verb");
3983 return;
3984 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003985 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003986 pointsPtr += verb;
3987 SkASSERT(fCurrentContour);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003988 if (verbPtr == endOfFirstHalf) {
3989 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003990 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003991 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003992}
3993
3994private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00003995 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003996 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003997 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003998 Contour* fCurrentContour;
3999 SkTArray<Contour>& fContours;
4000 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004001 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com235f56a2012-09-14 14:19:30 +00004002 ShapeOpMask fXorMask;
4003 int fSecondHalf;
4004 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004005};
4006
4007class Work {
4008public:
4009 enum SegmentType {
4010 kHorizontalLine_Segment = -1,
4011 kVerticalLine_Segment = 0,
4012 kLine_Segment = SkPath::kLine_Verb,
4013 kQuad_Segment = SkPath::kQuad_Verb,
4014 kCubic_Segment = SkPath::kCubic_Verb,
4015 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004016
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004017 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4018 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4019 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004020
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004021 // FIXME: does it make sense to write otherIndex now if we're going to
4022 // fix it up later?
4023 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004024 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004025 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004026
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004027 // Avoid collapsing t values that are close to the same since
4028 // we walk ts to describe consecutive intersections. Since a pair of ts can
4029 // be nearly equal, any problems caused by this should be taken care
4030 // of later.
4031 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004032 int addT(double newT, const Work& other) {
4033 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004034 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004035
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004036 bool advance() {
4037 return ++fIndex < fLast;
4038 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004039
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004040 SkScalar bottom() const {
4041 return bounds().fBottom;
4042 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004043
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004044 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004045 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004046 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004047
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004048 const SkPoint* cubic() const {
4049 return fCubic;
4050 }
4051
4052 void init(Contour* contour) {
4053 fContour = contour;
4054 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004055 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004056 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004057
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004058 bool isAdjacent(const Work& next) {
4059 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4060 }
4061
4062 bool isFirstLast(const Work& next) {
4063 return fContour == next.fContour && fIndex == 0
4064 && next.fIndex == fLast - 1;
4065 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004066
4067 SkScalar left() const {
4068 return bounds().fLeft;
4069 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004070
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004071 void promoteToCubic() {
4072 fCubic[0] = pts()[0];
4073 fCubic[2] = pts()[1];
4074 fCubic[3] = pts()[2];
4075 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4076 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4077 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4078 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4079 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004080
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004081 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004082 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004083 }
4084
4085 SkScalar right() const {
4086 return bounds().fRight;
4087 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004088
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004089 ptrdiff_t segmentIndex() const {
4090 return fIndex;
4091 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004092
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004093 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004094 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004095 SegmentType type = (SegmentType) segment.verb();
4096 if (type != kLine_Segment) {
4097 return type;
4098 }
4099 if (segment.isHorizontal()) {
4100 return kHorizontalLine_Segment;
4101 }
4102 if (segment.isVertical()) {
4103 return kVerticalLine_Segment;
4104 }
4105 return kLine_Segment;
4106 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004107
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004108 bool startAfter(const Work& after) {
4109 fIndex = after.fIndex;
4110 return advance();
4111 }
4112
4113 SkScalar top() const {
4114 return bounds().fTop;
4115 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004116
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004117 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004118 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004119 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004120
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004121 SkScalar x() const {
4122 return bounds().fLeft;
4123 }
4124
4125 bool xFlipped() const {
4126 return x() != pts()[0].fX;
4127 }
4128
4129 SkScalar y() const {
4130 return bounds().fTop;
4131 }
4132
4133 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004134 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004135 }
4136
4137protected:
4138 Contour* fContour;
4139 SkPoint fCubic[4];
4140 int fIndex;
4141 int fLast;
4142};
4143
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004144#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004145static void debugShowLineIntersection(int pts, const Work& wt,
4146 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004147 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004148 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004149 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4150 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4151 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4152 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004153 return;
4154 }
4155 SkPoint wtOutPt, wnOutPt;
4156 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4157 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004158 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 +00004159 __FUNCTION__,
4160 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4161 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4162 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004163 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004164 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004165 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004166 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4167 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4168 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004169 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4170 }
4171 SkDebugf("\n");
4172}
4173
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004174static void debugShowQuadLineIntersection(int pts, const Work& wt,
4175 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4176 if (!pts) {
4177 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
4178 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
4179 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4180 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4181 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
4182 wt.pts()[2].fX, wt.pts()[2].fY );
4183 return;
4184 }
4185 SkPoint wtOutPt, wnOutPt;
4186 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4187 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4188 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4189 __FUNCTION__,
4190 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4191 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4192 wtOutPt.fX, wtOutPt.fY);
4193 if (pts == 2) {
4194 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
4195 }
4196 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4197 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4198 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4199 if (pts == 2) {
4200 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4201 }
4202 SkDebugf("\n");
4203}
4204
caryclark@google.coma461ff02012-10-11 12:54:23 +00004205static void debugShowQuadIntersection(int pts, const Work& wt,
4206 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4207 if (!pts) {
4208 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
4209 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
4210 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00004211 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00004212 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
4213 wt.pts()[2].fX, wt.pts()[2].fY );
4214 return;
4215 }
4216 SkPoint wtOutPt, wnOutPt;
4217 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4218 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4219 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4220 __FUNCTION__,
4221 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4222 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4223 wtOutPt.fX, wtOutPt.fY);
4224 if (pts == 2) {
4225 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
4226 }
4227 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4228 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4229 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
4230 wnOutPt.fX, wnOutPt.fY);
4231 if (pts == 2) {
4232 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004233 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004234 SkDebugf("\n");
4235}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004236#else
4237static void debugShowLineIntersection(int , const Work& ,
4238 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004239}
caryclark@google.coma461ff02012-10-11 12:54:23 +00004240
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004241static void debugShowQuadLineIntersection(int , const Work& ,
4242 const Work& , const double [2], const double [2]) {
4243}
4244
caryclark@google.coma461ff02012-10-11 12:54:23 +00004245static void debugShowQuadIntersection(int , const Work& ,
4246 const Work& , const double [2], const double [2]) {
4247}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004248#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004249
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004250static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004251
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004252 if (test != next) {
4253 if (test->bounds().fBottom < next->bounds().fTop) {
4254 return false;
4255 }
4256 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
4257 return true;
4258 }
4259 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004260 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004261 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004262 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004263 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004264 Work wn;
4265 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004266 if (test == next && !wn.startAfter(wt)) {
4267 continue;
4268 }
4269 do {
4270 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
4271 continue;
4272 }
4273 int pts;
4274 Intersections ts;
4275 bool swap = false;
4276 switch (wt.segmentType()) {
4277 case Work::kHorizontalLine_Segment:
4278 swap = true;
4279 switch (wn.segmentType()) {
4280 case Work::kHorizontalLine_Segment:
4281 case Work::kVerticalLine_Segment:
4282 case Work::kLine_Segment: {
4283 pts = HLineIntersect(wn.pts(), wt.left(),
4284 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004285 debugShowLineIntersection(pts, wt, wn,
4286 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004287 break;
4288 }
4289 case Work::kQuad_Segment: {
4290 pts = HQuadIntersect(wn.pts(), wt.left(),
4291 wt.right(), wt.y(), wt.xFlipped(), ts);
4292 break;
4293 }
4294 case Work::kCubic_Segment: {
4295 pts = HCubicIntersect(wn.pts(), wt.left(),
4296 wt.right(), wt.y(), wt.xFlipped(), ts);
4297 break;
4298 }
4299 default:
4300 SkASSERT(0);
4301 }
4302 break;
4303 case Work::kVerticalLine_Segment:
4304 swap = true;
4305 switch (wn.segmentType()) {
4306 case Work::kHorizontalLine_Segment:
4307 case Work::kVerticalLine_Segment:
4308 case Work::kLine_Segment: {
4309 pts = VLineIntersect(wn.pts(), wt.top(),
4310 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004311 debugShowLineIntersection(pts, wt, wn,
4312 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004313 break;
4314 }
4315 case Work::kQuad_Segment: {
4316 pts = VQuadIntersect(wn.pts(), wt.top(),
4317 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4318 break;
4319 }
4320 case Work::kCubic_Segment: {
4321 pts = VCubicIntersect(wn.pts(), wt.top(),
4322 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4323 break;
4324 }
4325 default:
4326 SkASSERT(0);
4327 }
4328 break;
4329 case Work::kLine_Segment:
4330 switch (wn.segmentType()) {
4331 case Work::kHorizontalLine_Segment:
4332 pts = HLineIntersect(wt.pts(), wn.left(),
4333 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004334 debugShowLineIntersection(pts, wt, wn,
4335 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004336 break;
4337 case Work::kVerticalLine_Segment:
4338 pts = VLineIntersect(wt.pts(), wn.top(),
4339 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004340 debugShowLineIntersection(pts, wt, wn,
4341 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004342 break;
4343 case Work::kLine_Segment: {
4344 pts = LineIntersect(wt.pts(), wn.pts(), ts);
4345 debugShowLineIntersection(pts, wt, wn,
4346 ts.fT[1], ts.fT[0]);
4347 break;
4348 }
4349 case Work::kQuad_Segment: {
4350 swap = true;
4351 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004352 debugShowQuadLineIntersection(pts, wt, wn,
4353 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004354 break;
4355 }
4356 case Work::kCubic_Segment: {
4357 swap = true;
4358 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
4359 break;
4360 }
4361 default:
4362 SkASSERT(0);
4363 }
4364 break;
4365 case Work::kQuad_Segment:
4366 switch (wn.segmentType()) {
4367 case Work::kHorizontalLine_Segment:
4368 pts = HQuadIntersect(wt.pts(), wn.left(),
4369 wn.right(), wn.y(), wn.xFlipped(), ts);
4370 break;
4371 case Work::kVerticalLine_Segment:
4372 pts = VQuadIntersect(wt.pts(), wn.top(),
4373 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4374 break;
4375 case Work::kLine_Segment: {
4376 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004377 debugShowQuadLineIntersection(pts, wt, wn,
4378 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004379 break;
4380 }
4381 case Work::kQuad_Segment: {
4382 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004383 debugShowQuadIntersection(pts, wt, wn,
4384 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004385 break;
4386 }
4387 case Work::kCubic_Segment: {
4388 wt.promoteToCubic();
4389 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
4390 break;
4391 }
4392 default:
4393 SkASSERT(0);
4394 }
4395 break;
4396 case Work::kCubic_Segment:
4397 switch (wn.segmentType()) {
4398 case Work::kHorizontalLine_Segment:
4399 pts = HCubicIntersect(wt.pts(), wn.left(),
4400 wn.right(), wn.y(), wn.xFlipped(), ts);
4401 break;
4402 case Work::kVerticalLine_Segment:
4403 pts = VCubicIntersect(wt.pts(), wn.top(),
4404 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4405 break;
4406 case Work::kLine_Segment: {
4407 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
4408 break;
4409 }
4410 case Work::kQuad_Segment: {
4411 wn.promoteToCubic();
4412 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
4413 break;
4414 }
4415 case Work::kCubic_Segment: {
4416 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4417 break;
4418 }
4419 default:
4420 SkASSERT(0);
4421 }
4422 break;
4423 default:
4424 SkASSERT(0);
4425 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004426 if (!foundCommonContour && pts > 0) {
4427 test->addCross(next);
4428 next->addCross(test);
4429 foundCommonContour = true;
4430 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004431 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004432 if (pts == 2) {
4433 if (wn.segmentType() <= Work::kLine_Segment
4434 && wt.segmentType() <= Work::kLine_Segment) {
4435 wt.addCoincident(wn, ts, swap);
4436 continue;
4437 }
4438 if (wn.segmentType() == Work::kQuad_Segment
4439 && wt.segmentType() == Work::kQuad_Segment
4440 && ts.coincidentUsed() == 2) {
4441 wt.addCoincident(wn, ts, swap);
4442 continue;
4443 }
4444
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004445 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004446 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004447 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4448 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004449 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4450 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004451 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4452 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004453 }
4454 } while (wn.advance());
4455 } while (wt.advance());
4456 return true;
4457}
4458
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004459// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004460// see if coincidence is formed by clipping non-concident segments
caryclark@google.com235f56a2012-09-14 14:19:30 +00004461static void coincidenceCheck(SkTDArray<Contour*>& contourList) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004462 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004463 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004464 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004465 contour->resolveCoincidence();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004466 }
4467 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4468 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004469 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004470 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004471#if 0
4472 // OPTIMIZATION: this check could be folded in with findTooClose -- maybe
4473 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4474 Contour* contour = contourList[cIndex];
4475 contour->collapseTriangles();
4476 }
4477#endif
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004478}
4479
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004480// project a ray from the top of the contour up and see if it hits anything
4481// note: when we compute line intersections, we keep track of whether
4482// two contours touch, so we need only look at contours not touching this one.
4483// OPTIMIZATION: sort contourList vertically to avoid linear walk
4484static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004485 const Segment* current, int index, int endIndex) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004486 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004487 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004488 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00004489 const Segment* test = NULL;
4490 int tIndex;
4491 double tHit;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004492 // bool checkCrosses = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004493 for (int cTest = 0; cTest < contourCount; ++cTest) {
4494 Contour* contour = contourList[cTest];
4495 if (basePt.fY < contour->bounds().fTop) {
4496 continue;
4497 }
4498 if (bestY > contour->bounds().fBottom) {
4499 continue;
4500 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004501#if 0
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004502 // even though the contours crossed, if spans cancel through concidence,
4503 // the contours may be not have any span links to chase, and the current
4504 // segment may be isolated. Detect this by seeing if current has
4505 // uninitialized wind sums. If so, project a ray instead of relying on
4506 // previously found intersections.
4507 if (baseContour == contour) {
4508 continue;
4509 }
4510 if (checkCrosses && baseContour->crosses(contour)) {
4511 if (current->isConnected(index, endIndex)) {
4512 continue;
4513 }
4514 checkCrosses = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004515 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004516#endif
caryclark@google.com47580692012-07-23 12:14:49 +00004517 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
4518 if (next) {
4519 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004520 }
caryclark@google.com47580692012-07-23 12:14:49 +00004521 }
4522 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00004523 return 0;
4524 }
4525 int winding, windValue;
4526 // If the ray hit the end of a span, we need to construct the wheel of
4527 // angles to find the span closest to the ray -- even if there are just
4528 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004529 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004530 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00004531 SkTDArray<Angle> angles;
4532 int end = test->nextSpan(tIndex, 1);
4533 if (end < 0) {
4534 end = test->nextSpan(tIndex, -1);
4535 }
4536 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00004537 SkASSERT(angles.count() > 0);
4538 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
4539#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00004540 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00004541#endif
4542 return 0;
4543 }
caryclark@google.com47580692012-07-23 12:14:49 +00004544 test->buildAngles(tIndex, angles);
4545 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00004546 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00004547 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004548 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00004549 // hour after 6 o'clock
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004550 (void) Segment::SortAngles(angles, sorted);
caryclark@google.com47580692012-07-23 12:14:49 +00004551#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004552 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00004553#endif
4554 // walk the sorted angle fan to find the lowest angle
4555 // above the base point. Currently, the first angle in the sorted array
4556 // is 12 noon or an earlier hour (the next counterclockwise)
4557 int count = sorted.count();
4558 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004559 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00004560 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004561 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00004562 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00004563 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004564 if (angle->unsortable()) {
4565 continue;
4566 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004567 if (baseMatches && angle->isHorizontal()) {
4568 continue;
4569 }
4570 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00004571 test = angle->segment();
4572 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
4573 const SkPoint* pts = test->pts();
4574 indexDx = pts[2].fX - pts[1].fX - indexDx;
4575 }
caryclark@google.com47580692012-07-23 12:14:49 +00004576 if (indexDx < 0) {
4577 left = index;
4578 } else if (indexDx > 0) {
4579 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00004580 int previous = index - 1;
4581 if (previous < 0) {
4582 previous = count - 1;
4583 }
4584 const Angle* prev = sorted[previous];
4585 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
4586#if DEBUG_SORT
4587 SkDebugf("%s use prev\n", __FUNCTION__);
4588#endif
4589 right = previous;
4590 }
caryclark@google.com47580692012-07-23 12:14:49 +00004591 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004592 } else {
4593 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004594 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004595 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004596 if (left < 0 && right < 0) {
4597 left = mid;
4598 }
caryclark@google.com47580692012-07-23 12:14:49 +00004599 SkASSERT(left >= 0 || right >= 0);
4600 if (left < 0) {
4601 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004602 } else if (left >= 0 && mid >= 0 && right >= 0
4603 && sorted[mid]->sign() == sorted[right]->sign()) {
4604 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004605 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004606 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004607 test = angle->segment();
4608 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004609 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004610 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004611#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004612 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4613 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004614#endif
4615 } else {
4616 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004617 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004618 windValue = test->windValue(tIndex);
4619#if DEBUG_WINDING
4620 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4621 windValue);
4622#endif
4623 }
4624 // see if a + change in T results in a +/- change in X (compute x'(T))
4625 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004626 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4627 const SkPoint* pts = test->pts();
4628 dx = pts[2].fX - pts[1].fX - dx;
4629 }
caryclark@google.com47580692012-07-23 12:14:49 +00004630#if DEBUG_WINDING
4631 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4632#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004633 SkASSERT(dx != 0);
4634 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004635 winding += dx > 0 ? -windValue : windValue;
4636#if DEBUG_WINDING
4637 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4638#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004639 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004640 return winding;
4641}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004642
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004643#if SORTABLE_CONTOURS
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004644// OPTIMIZATION: not crazy about linear search here to find top active y.
4645// seems like we should break down and do the sort, or maybe sort each
rmistry@google.comd6176b02012-08-23 18:14:13 +00004646// contours' segments?
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004647// Once the segment array is built, there's no reason I can think of not to
4648// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004649// FIXME: return the contour found to pass to inner contour check
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004650static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004651 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004652 int cIndex = 0;
4653 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004654 SkScalar bestY = SK_ScalarMax;
4655 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004656 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004657 contour = contourList[cIndex];
4658 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004659 } while (!topStart && ++cIndex < contourCount);
4660 if (!topStart) {
4661 return NULL;
4662 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004663 while (++cIndex < contourCount) {
4664 contour = contourList[cIndex];
4665 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004666 continue;
4667 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004668 SkScalar testY = SK_ScalarMax;
4669 Segment* test = contour->topSegment(testY);
4670 if (!test || bestY <= testY) {
4671 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004672 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004673 topStart = test;
4674 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004675 }
4676 return topStart;
4677}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004678#endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004679
caryclark@google.com24bec792012-08-20 12:43:57 +00004680static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4681 int contourCount = contourList.count();
4682 Segment* result;
4683 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4684 Contour* contour = contourList[cIndex];
4685 result = contour->undoneSegment(start, end);
4686 if (result) {
4687 return result;
4688 }
4689 }
4690 return NULL;
4691}
4692
4693
4694
caryclark@google.come21cb182012-07-23 21:26:31 +00004695static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
4696 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004697 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004698 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004699 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4700 Segment* segment = backPtr.fOther;
4701 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004702 SkTDArray<Angle> angles;
4703 int done = 0;
4704 if (segment->activeAngle(tIndex, done, angles)) {
4705 Angle* last = angles.end() - 1;
4706 tIndex = last->start();
4707 endIndex = last->end();
4708 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004709 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004710 if (done == angles.count()) {
4711 chase.pop(&span);
4712 continue;
4713 }
4714 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004715 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004716#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004717 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004718#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004719 if (!sortable) {
4720 chase.pop(&span);
4721 continue;
4722 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004723 // find first angle, initialize winding to computed fWindSum
4724 int firstIndex = -1;
4725 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004726 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004727 do {
4728 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004729 segment = angle->segment();
4730 winding = segment->windSum(angle);
4731 } while (winding == SK_MinS32);
4732 int spanWinding = segment->spanSign(angle->start(), angle->end());
4733 #if DEBUG_WINDING
4734 SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
4735 __FUNCTION__, winding, spanWinding, contourWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00004736 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004737 // turn swinding into contourWinding
4738 if (spanWinding * winding < 0) {
4739 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004740 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004741 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004742 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004743 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004744 // we care about first sign and whether wind sum indicates this
4745 // edge is inside or outside. Maybe need to pass span winding
4746 // or first winding or something into this function?
4747 // advance to first undone angle, then return it and winding
4748 // (to set whether edges are active or not)
4749 int nextIndex = firstIndex + 1;
4750 int angleCount = sorted.count();
4751 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004752 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00004753 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00004754 do {
4755 SkASSERT(nextIndex != firstIndex);
4756 if (nextIndex == angleCount) {
4757 nextIndex = 0;
4758 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004759 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00004760 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004761 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00004762 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004763 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00004764 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
4765 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004766 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004767 tIndex = angle->start();
4768 endIndex = angle->end();
4769 int lesser = SkMin32(tIndex, endIndex);
4770 const Span& nextSpan = segment->span(lesser);
4771 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004772#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00004773 // FIXME: this be wrong. assign startWinding if edge is in
4774 // same direction. If the direction is opposite, winding to
4775 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00004776 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004777 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004778 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004779 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004780#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004781 break;
4782 }
4783 } while (++nextIndex != lastIndex);
4784 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004785 }
4786 return NULL;
4787}
4788
caryclark@google.com027de222012-07-12 12:52:50 +00004789#if DEBUG_ACTIVE_SPANS
4790static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004791 int index;
4792 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00004793 contourList[index]->debugShowActiveSpans();
4794 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004795 for (index = 0; index < contourList.count(); ++ index) {
4796 contourList[index]->validateActiveSpans();
4797 }
caryclark@google.com027de222012-07-12 12:52:50 +00004798}
4799#endif
4800
caryclark@google.com27c449a2012-07-27 18:26:38 +00004801static bool windingIsActive(int winding, int spanWinding) {
4802 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
4803 && (!winding || !spanWinding || winding == -spanWinding);
4804}
4805
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004806#if !SORTABLE_CONTOURS
4807static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comf839c032012-10-26 21:03:50 +00004808 int& endIndex, SkPoint& topLeft) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004809 Segment* result;
4810 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00004811 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004812 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00004813 Segment* topStart = NULL;
4814 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4815 Contour* contour = contourList[cIndex];
4816 const Bounds& bounds = contour->bounds();
4817 if (bounds.fBottom < topLeft.fY) {
4818 continue;
4819 }
4820 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
4821 continue;
4822 }
4823 Segment* test = contour->topSortableSegment(topLeft, bestXY);
4824 if (test) {
4825 topStart = test;
4826 }
4827 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004828 if (!topStart) {
4829 return NULL;
4830 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004831 topLeft = bestXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004832 result = topStart->findTop(index, endIndex);
4833 } while (!result);
4834 return result;
4835}
4836#endif
4837
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004838// Each segment may have an inside or an outside. Segments contained within
4839// winding may have insides on either side, and form a contour that should be
4840// ignored. Segments that are coincident with opposing direction segments may
4841// have outsides on either side, and should also disappear.
4842// 'Normal' segments will have one inside and one outside. Subsequent connections
4843// when winding should follow the intersection direction. If more than one edge
4844// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004845 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00004846 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004847// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00004848static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004849 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004850 bool unsortable = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00004851 bool closable = true;
4852 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com15fa1382012-05-07 20:49:36 +00004853 do {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004854#if SORTABLE_CONTOURS // old way
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004855 Segment* topStart = findTopContour(contourList);
caryclark@google.com15fa1382012-05-07 20:49:36 +00004856 if (!topStart) {
4857 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00004858 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004859 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00004860 // follow edges to intersection by changing the index by direction.
4861 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004862 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004863#else // new way: iterate while top is unsortable
4864 int index, endIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00004865 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004866 if (!current) {
4867 break;
4868 }
4869#endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004870 int contourWinding;
4871 if (firstContour) {
4872 contourWinding = 0;
4873 firstContour = false;
4874 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004875 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004876 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00004877 if (sumWinding == SK_MinS32) {
4878 sumWinding = current->computeSum(index, endIndex);
4879 }
4880 if (sumWinding == SK_MinS32) {
4881 contourWinding = innerContourCheck(contourList, current,
4882 index, endIndex);
4883 } else {
4884 contourWinding = sumWinding;
4885 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004886 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
4887 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00004888 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004889 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004890#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004891 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004892 sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00004893 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004894#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004895 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004896#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004897 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004898 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
4899#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004900 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004901 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004902 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004903 // FIXME: needs work. While it works in limited situations, it does
4904 // not always compute winding correctly. Active should be removed and instead
4905 // the initial winding should be correctly passed in so that if the
4906 // inner contour is wound the same way, it never finds an accumulated
4907 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00004908 // other than zero when resolving sorted angles.
caryclark@google.com27c449a2012-07-27 18:26:38 +00004909 bool active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004910 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004911 do {
caryclark@google.com0e08a192012-07-13 21:07:52 +00004912 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00004913 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
4914 __FUNCTION__, active ? "true" : "false",
4915 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004916 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004917 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004918 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004919 int nextStart = index;
4920 int nextEnd = endIndex;
4921 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004922 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004923 if (!next) {
caryclark@google.comf839c032012-10-26 21:03:50 +00004924 if (active && simple.hasMove()
4925 && current->verb() != SkPath::kLine_Verb
4926 && !simple.isClosed()) {
4927 current->addCurveTo(index, endIndex, simple, true);
4928 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00004929 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004930 break;
4931 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004932 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004933 current = next;
4934 index = nextStart;
4935 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00004936 } while (!simple.isClosed()
4937 && ((active && !unsortable) || !current->done()));
4938 if (active) {
4939 if (!simple.isClosed()) {
4940 SkASSERT(unsortable);
4941 int min = SkMin32(index, endIndex);
4942 if (!current->done(min)) {
4943 current->addCurveTo(index, endIndex, simple, true);
4944 current->markDone(SkMin32(index, endIndex), spanWinding);
4945 }
4946 closable = false;
4947 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004948 simple.close();
4949 }
caryclark@google.come21cb182012-07-23 21:26:31 +00004950 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004951 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00004952 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004953 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004954 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00004955 break;
4956 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004957 int lesser = SkMin32(index, endIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004958 spanWinding = current->spanSign(index, endIndex);
4959 winding = current->windSum(lesser);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004960 bool inner = useInnerWinding(winding - spanWinding, winding);
4961 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004962 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
caryclark@google.com59823f72012-08-09 18:17:47 +00004963 " inner=%d result=%d\n",
caryclark@google.com2ddff932012-08-07 21:25:27 +00004964 __FUNCTION__, current->debugID(), current->t(lesser),
4965 spanWinding, winding, SkSign32(index - endIndex),
4966 useInnerWinding(winding - spanWinding, winding),
caryclark@google.com2ddff932012-08-07 21:25:27 +00004967 inner ? winding - spanWinding : winding);
4968 #endif
4969 if (inner) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004970 winding -= spanWinding;
4971 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004972 active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004973 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00004974 } while (true);
caryclark@google.comf839c032012-10-26 21:03:50 +00004975 return closable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004976}
4977
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004978// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00004979static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00004980 Segment* current;
4981 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004982 bool unsortable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00004983 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00004984 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004985 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004986 int nextStart = start;
4987 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004988 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00004989 if (!next) {
caryclark@google.comf839c032012-10-26 21:03:50 +00004990 if (simple.hasMove()
4991 && current->verb() != SkPath::kLine_Verb
4992 && !simple.isClosed()) {
4993 current->addCurveTo(start, end, simple, true);
4994 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00004995 }
caryclark@google.com24bec792012-08-20 12:43:57 +00004996 break;
4997 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004998 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00004999 current = next;
5000 start = nextStart;
5001 end = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005002 } while (!simple.isClosed());
5003 // FIXME: add unsortable test
5004 if (simple.hasMove()) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005005 simple.close();
5006 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005007 #if DEBUG_ACTIVE_SPANS
5008 debugShowActiveSpans(contourList);
5009 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005010 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005011 return !unsortable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005012}
5013
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005014static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5015 int contourCount = contourList.count();
5016 for (int cTest = 0; cTest < contourCount; ++cTest) {
5017 Contour* contour = contourList[cTest];
5018 contour->fixOtherTIndex();
5019 }
5020}
5021
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005022#if !SORTABLE_CONTOURS
5023static void sortSegments(SkTDArray<Contour*>& contourList) {
5024 int contourCount = contourList.count();
5025 for (int cTest = 0; cTest < contourCount; ++cTest) {
5026 Contour* contour = contourList[cTest];
5027 contour->sortSegments();
5028 }
5029}
5030#endif
5031
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005032static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005033 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005034 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005035 if (count == 0) {
5036 return;
5037 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005038 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005039 *list.append() = &contours[index];
5040 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005041 QSort<Contour>(list.begin(), list.end() - 1);
5042}
5043
caryclark@google.comf839c032012-10-26 21:03:50 +00005044static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
5045 return approximately_equal(a.fX, b.fX) && approximately_equal(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005046}
5047
caryclark@google.comf839c032012-10-26 21:03:50 +00005048 /*
5049 check start and end of each contour
5050 if not the same, record them
5051 match them up
5052 connect closest
5053 reassemble contour pieces into new path
5054 */
5055static void assemble(const PathWrapper& path, PathWrapper& simple) {
5056#if DEBUG_PATH_CONSTRUCTION
5057 SkDebugf("%s\n", __FUNCTION__);
5058#endif
5059 SkTArray<Contour> contours;
5060 EdgeBuilder builder(path, contours);
5061 builder.finish();
5062 int count = contours.count();
5063 int oIndex;
5064 SkTDArray<int> runs; // indices of partial contours
5065 for (oIndex = 0; oIndex < count; ++oIndex) {
5066 const Contour& eContour = contours[oIndex];
5067 const SkPoint& eStart = eContour.start();
5068 const SkPoint& eEnd = eContour.end();
5069 if (approximatelyEqual(eStart, eEnd)) {
5070 eContour.toPath(simple);
5071 continue;
5072 }
5073 *runs.append() = oIndex;
5074 }
5075 count = runs.count();
5076 SkTDArray<int> sLink, eLink;
5077 sLink.setCount(count);
5078 eLink.setCount(count);
5079 int rIndex;
5080 for (rIndex = 0; rIndex < count; ++rIndex) {
5081 sLink[rIndex] = eLink[rIndex] = INT_MAX;
5082 }
5083 for (rIndex = 0; rIndex < count - 1; ++rIndex) {
5084 oIndex = runs[rIndex];
5085 const Contour& oContour = contours[oIndex];
5086 const SkPoint& oStart = oContour.start();
5087 const SkPoint& oEnd = oContour.end();
5088 for (int inner = rIndex + 1; inner < count; ++inner) {
5089 int iIndex = runs[inner];
5090 const Contour& iContour = contours[iIndex];
5091 const SkPoint& iStart = iContour.start();
5092 const SkPoint& iEnd = iContour.end();
5093 if (approximatelyEqual(oStart, iStart)) {
5094 SkASSERT(sLink[rIndex] == INT_MAX);
5095 sLink[rIndex] = ~iIndex;
5096 SkASSERT(sLink[iIndex] == INT_MAX);
5097 sLink[iIndex] = ~rIndex;
5098 } else if (approximatelyEqual(oStart, iEnd)) {
5099 SkASSERT(sLink[rIndex] == INT_MAX);
5100 sLink[rIndex] = iIndex;
5101 SkASSERT(eLink[iIndex] == INT_MAX);
5102 eLink[iIndex] = rIndex;
5103 }
5104 if (approximatelyEqual(oEnd, iStart)) {
5105 SkASSERT(eLink[rIndex] == INT_MAX);
5106 eLink[rIndex] = iIndex;
5107 SkASSERT(sLink[iIndex] == INT_MAX);
5108 sLink[iIndex] = rIndex;
5109 } else if (approximatelyEqual(oEnd, iEnd)) {
5110 SkASSERT(eLink[rIndex] == INT_MAX);
5111 eLink[rIndex] = ~iIndex;
5112 SkASSERT(eLink[iIndex] == INT_MAX);
5113 eLink[iIndex] = ~rIndex;
5114 }
5115 }
5116 }
5117 rIndex = 0;
5118 bool forward = true;
5119 bool first = true;
5120 const SkPoint* startPtr;
5121 int sIndex = sLink[rIndex];
5122 if (sIndex < 0) {
5123 SkASSERT(sLink[~sIndex] != INT_MAX);
5124 sLink[~sIndex] = INT_MAX;
5125 } else {
5126 SkASSERT(eLink[sIndex] != INT_MAX);
5127 eLink[sIndex] = INT_MAX;
5128 }
5129 SkASSERT(sLink[rIndex] != INT_MAX);
5130 sLink[rIndex] = INT_MAX;
5131 do {
5132 do {
5133 oIndex = runs[rIndex];
5134 const Contour& contour = contours[oIndex];
5135 if (first) {
5136 startPtr = &contour.start();
5137 first = false;
5138 simple.deferredMove(startPtr[0]);
5139 }
5140 const SkPoint* endPtr;
5141 if (forward) {
5142 contour.toPartialForward(simple);
5143 endPtr = &contour.end();
5144 } else {
5145 contour.toPartialBackward(simple);
5146 endPtr = &contour.start();
5147 }
5148 if (approximatelyEqual(*startPtr, *endPtr)) {
5149 simple.close();
5150 first = forward = true;
5151 break;
5152 }
5153 int newRIndex;
5154 if (forward) {
5155 newRIndex = eLink[rIndex];
5156 SkASSERT(newRIndex != INT_MAX);
5157 SkASSERT(eLink[rIndex] != INT_MAX);
5158 eLink[rIndex] = INT_MAX;
5159 if (newRIndex >= 0) {
5160 SkASSERT(sLink[newRIndex] == rIndex);
5161 sLink[newRIndex] = INT_MAX;
5162 } else {
5163 SkASSERT(eLink[~newRIndex] == ~rIndex);
5164 eLink[~newRIndex] = INT_MAX;
5165 }
5166 } else {
5167 newRIndex = sLink[rIndex];
5168 SkASSERT(newRIndex != INT_MAX);
5169 SkASSERT(sLink[rIndex] != INT_MAX);
5170 sLink[rIndex] = INT_MAX;
5171 if (newRIndex >= 0) {
5172 SkASSERT(eLink[newRIndex] == rIndex);
5173 eLink[newRIndex] = INT_MAX;
5174 } else {
5175 SkASSERT(sLink[~newRIndex] == ~rIndex);
5176 sLink[~newRIndex] = INT_MAX;
5177 }
5178 }
5179 rIndex = newRIndex;
5180 if (rIndex < 0) {
5181 forward ^= 1;
5182 rIndex = ~rIndex;
5183 }
5184 } while (true);
5185 for (rIndex = 0; rIndex < count; ++rIndex) {
5186 if (sLink[rIndex] != INT_MAX) {
5187 break;
5188 }
5189 }
5190 } while (rIndex < count);
5191 SkASSERT(first);
5192}
5193
5194void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005195 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00005196 result.reset();
5197 result.setFillType(SkPath::kEvenOdd_FillType);
5198 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005199
5200 // turn path into list of segments
5201 SkTArray<Contour> contours;
5202 // FIXME: add self-intersecting cubics' T values to segment
5203 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00005204 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005205 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005206 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005207 Contour** currentPtr = contourList.begin();
5208 if (!currentPtr) {
5209 return;
5210 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005211 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005212 // find all intersections between segments
5213 do {
5214 Contour** nextPtr = currentPtr;
5215 Contour* current = *currentPtr++;
5216 Contour* next;
5217 do {
5218 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005219 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005220 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005221 // eat through coincident edges
caryclark@google.com235f56a2012-09-14 14:19:30 +00005222 coincidenceCheck(contourList);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00005223 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005224#if !SORTABLE_CONTOURS
5225 sortSegments(contourList);
5226#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005227 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005228 if (builder.xorMask() == kWinding_Mask
5229 ? !bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00005230 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005231 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00005232 SkPath temp;
5233 temp.setFillType(SkPath::kEvenOdd_FillType);
5234 PathWrapper assembled(temp);
5235 assemble(simple, assembled);
5236 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00005237 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005238}
5239