blob: 0c81b2af9f215cd3d9c4284bd691489556b2d173 [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
caryclark@google.com47580692012-07-23 12:14:49 +000020// An Edge is a Segment generated from a Span
caryclark@google.com15fa1382012-05-07 20:49:36 +000021
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022// FIXME: remove once debugging is complete
caryclark@google.com47580692012-07-23 12:14:49 +000023#ifdef SK_DEBUG
24int gDebugMaxWindSum = SK_MaxS32;
25int gDebugMaxWindValue = SK_MaxS32;
26#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +000027
caryclark@google.com3350c3c2012-08-24 15:24:36 +000028#define HIGH_DEF_ANGLES 1
29
30#if HIGH_DEF_ANGLES
31typedef double AngleValue;
32#else
33typedef SkScalar AngleValue;
34#endif
35
caryclark@google.com47580692012-07-23 12:14:49 +000036#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa0588f2012-04-26 21:01:06 +000037
caryclark@google.com32546db2012-08-31 20:55:07 +000038#if 1 // set to 1 for multiple thread -- no debugging
caryclark@google.com47580692012-07-23 12:14:49 +000039
40const bool gRunTestsInOneThread = false;
41
42#define DEBUG_ACTIVE_SPANS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000043#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000044#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000045#define DEBUG_ANGLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000046#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000047#define DEBUG_CROSS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000048#define DEBUG_DUMP 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000049#define DEBUG_MARK_DONE 0
caryclark@google.com47580692012-07-23 12:14:49 +000050#define DEBUG_PATH_CONSTRUCTION 0
51#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000052#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000053#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000054
55#else
56
caryclark@google.com47580692012-07-23 12:14:49 +000057const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000058
caryclark@google.comafe56de2012-07-24 18:11:03 +000059#define DEBUG_ACTIVE_SPANS 1
caryclark@google.comc899ad92012-08-23 15:24:42 +000060#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com24bec792012-08-20 12:43:57 +000061#define DEBUG_ADD_T_PAIR 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000062#define DEBUG_ANGLE 1
63#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000064#define DEBUG_CROSS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000065#define DEBUG_DUMP 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000066#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000067#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000068#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000069#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000070#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000071
72#endif
73
caryclark@google.com534aa5b2012-08-02 20:08:21 +000074#if (DEBUG_ACTIVE_SPANS || DEBUG_CONCIDENT || DEBUG_SORT) && !DEBUG_DUMP
caryclark@google.com027de222012-07-12 12:52:50 +000075#undef DEBUG_DUMP
76#define DEBUG_DUMP 1
77#endif
78
caryclark@google.comfa0588f2012-04-26 21:01:06 +000079#if DEBUG_DUMP
80static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000081// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000082static int gContourID;
83static int gSegmentID;
84#endif
85
caryclark@google.com8dcf1142012-07-02 20:27:02 +000086#ifndef DEBUG_TEST
87#define DEBUG_TEST 0
88#endif
89
caryclark@google.com32546db2012-08-31 20:55:07 +000090#define MAKE_CONST_LINE(line, pts) \
91 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
92#define MAKE_CONST_QUAD(quad, pts) \
93 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
94 {pts[2].fX, pts[2].fY}}
95#define MAKE_CONST_CUBIC(cubic, pts) \
96 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
97 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
98
caryclark@google.comfa0588f2012-04-26 21:01:06 +000099static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
100 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000101 MAKE_CONST_LINE(aLine, a);
102 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000103 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
104}
105
106static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
107 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000108 MAKE_CONST_QUAD(aQuad, a);
109 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000110 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000111}
112
caryclark@google.com32546db2012-08-31 20:55:07 +0000113static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000114 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000115 MAKE_CONST_CUBIC(aCubic, a);
116 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000117 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
118}
119
120static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
121 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000122 MAKE_CONST_QUAD(aQuad, a);
123 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000124 intersect(aQuad, bQuad, intersections);
caryclark@google.com32546db2012-08-31 20:55:07 +0000125 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000126}
127
128static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
129 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000130 MAKE_CONST_CUBIC(aCubic, a);
131 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000132 intersect(aCubic, bCubic, intersections);
133 return intersections.fUsed;
134}
135
136static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
137 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000138 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000139 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
140}
141
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000142static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
143 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000144 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000145 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
146}
147
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000148static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
149 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000150 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000151 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
152}
153
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000154static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
155 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000156 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000157 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
158}
159
160static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
161 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000162 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000163 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
164}
165
166static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
167 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000168 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000169 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000170}
171
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000172static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
173 SkScalar , SkScalar , bool , Intersections& ) = {
174 NULL,
175 VLineIntersect,
176 VQuadIntersect,
177 VCubicIntersect
178};
179
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000180static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000181 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000182 double x, y;
183 xy_at_t(line, t, x, y);
184 out->fX = SkDoubleToScalar(x);
185 out->fY = SkDoubleToScalar(y);
186}
187
188static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000189 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000190 double x, y;
191 xy_at_t(quad, t, x, y);
192 out->fX = SkDoubleToScalar(x);
193 out->fY = SkDoubleToScalar(y);
194}
195
196static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000197 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000198 double x, y;
199 xy_at_t(cubic, t, x, y);
200 out->fX = SkDoubleToScalar(x);
201 out->fY = SkDoubleToScalar(y);
202}
203
204static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
205 NULL,
206 LineXYAtT,
207 QuadXYAtT,
208 CubicXYAtT
209};
210
211static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000212 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000213 double x;
214 xy_at_t(aLine, t, x, *(double*) 0);
215 return SkDoubleToScalar(x);
216}
217
218static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000219 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000220 double x;
221 xy_at_t(quad, t, x, *(double*) 0);
222 return SkDoubleToScalar(x);
223}
224
225static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000226 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000227 double x;
228 xy_at_t(cubic, t, x, *(double*) 0);
229 return SkDoubleToScalar(x);
230}
231
232static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
233 NULL,
234 LineXAtT,
235 QuadXAtT,
236 CubicXAtT
237};
238
239static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000240 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000241 double y;
242 xy_at_t(aLine, t, *(double*) 0, y);
243 return SkDoubleToScalar(y);
244}
245
246static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000247 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000248 double y;
249 xy_at_t(quad, t, *(double*) 0, y);
250 return SkDoubleToScalar(y);
251}
252
253static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000254 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000255 double y;
256 xy_at_t(cubic, t, *(double*) 0, y);
257 return SkDoubleToScalar(y);
258}
259
260static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
261 NULL,
262 LineYAtT,
263 QuadYAtT,
264 CubicYAtT
265};
266
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000267static SkScalar LineDXAtT(const SkPoint a[2], double ) {
268 return a[1].fX - a[0].fX;
269}
270
271static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000272 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000273 double x;
274 dxdy_at_t(quad, t, x, *(double*) 0);
275 return SkDoubleToScalar(x);
276}
277
278static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000279 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000280 double x;
281 dxdy_at_t(cubic, t, x, *(double*) 0);
282 return SkDoubleToScalar(x);
283}
284
285static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
286 NULL,
287 LineDXAtT,
288 QuadDXAtT,
289 CubicDXAtT
290};
291
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000292static void LineSubDivide(const SkPoint a[2], double startT, double endT,
293 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000294 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000295 _Line dst;
296 sub_divide(aLine, startT, endT, dst);
297 sub[0].fX = SkDoubleToScalar(dst[0].x);
298 sub[0].fY = SkDoubleToScalar(dst[0].y);
299 sub[1].fX = SkDoubleToScalar(dst[1].x);
300 sub[1].fY = SkDoubleToScalar(dst[1].y);
301}
302
303static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
304 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000305 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000306 Quadratic dst;
307 sub_divide(aQuad, startT, endT, dst);
308 sub[0].fX = SkDoubleToScalar(dst[0].x);
309 sub[0].fY = SkDoubleToScalar(dst[0].y);
310 sub[1].fX = SkDoubleToScalar(dst[1].x);
311 sub[1].fY = SkDoubleToScalar(dst[1].y);
312 sub[2].fX = SkDoubleToScalar(dst[2].x);
313 sub[2].fY = SkDoubleToScalar(dst[2].y);
314}
315
316static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
317 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000318 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000319 Cubic dst;
320 sub_divide(aCubic, startT, endT, dst);
321 sub[0].fX = SkDoubleToScalar(dst[0].x);
322 sub[0].fY = SkDoubleToScalar(dst[0].y);
323 sub[1].fX = SkDoubleToScalar(dst[1].x);
324 sub[1].fY = SkDoubleToScalar(dst[1].y);
325 sub[2].fX = SkDoubleToScalar(dst[2].x);
326 sub[2].fY = SkDoubleToScalar(dst[2].y);
327 sub[3].fX = SkDoubleToScalar(dst[3].x);
328 sub[3].fY = SkDoubleToScalar(dst[3].y);
329}
330
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000331static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
332 SkPoint []) = {
333 NULL,
334 LineSubDivide,
335 QuadSubDivide,
336 CubicSubDivide
337};
338
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000339static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000340 _Line sub) {
341 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000342 _Line dst;
343 sub_divide(aLine, startT, endT, dst);
344 sub[0] = dst[0];
345 sub[1] = dst[1];
346}
347
348static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000349 Quadratic sub) {
350 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000351 Quadratic dst;
352 sub_divide(aQuad, startT, endT, dst);
353 sub[0] = dst[0];
354 sub[1] = dst[1];
355 sub[2] = dst[2];
356}
357
358static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000359 Cubic sub) {
360 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000361 Cubic dst;
362 sub_divide(aCubic, startT, endT, dst);
363 sub[0] = dst[0];
364 sub[1] = dst[1];
365 sub[2] = dst[2];
366 sub[3] = dst[3];
367}
368
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000369#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000370static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
371 SkRect& bounds) {
372 SkPoint dst[3];
373 QuadSubDivide(a, startT, endT, dst);
374 bounds.fLeft = bounds.fRight = dst[0].fX;
375 bounds.fTop = bounds.fBottom = dst[0].fY;
376 for (int index = 1; index < 3; ++index) {
377 bounds.growToInclude(dst[index].fX, dst[index].fY);
378 }
379}
380
381static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
382 SkRect& bounds) {
383 SkPoint dst[4];
384 CubicSubDivide(a, startT, endT, dst);
385 bounds.fLeft = bounds.fRight = dst[0].fX;
386 bounds.fTop = bounds.fBottom = dst[0].fY;
387 for (int index = 1; index < 4; ++index) {
388 bounds.growToInclude(dst[index].fX, dst[index].fY);
389 }
390}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000391#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000392
caryclark@google.com15fa1382012-05-07 20:49:36 +0000393static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000394 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000395 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000396 Quadratic dst;
397 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000398 if (order == 2) { // quad became line
399 for (int index = 0; index < order; ++index) {
400 SkPoint* pt = reducePts.append();
401 pt->fX = SkDoubleToScalar(dst[index].x);
402 pt->fY = SkDoubleToScalar(dst[index].y);
403 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000404 }
405 return (SkPath::Verb) (order - 1);
406}
407
408static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
409 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000410 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000411 Cubic dst;
412 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000413 if (order == 2 || order == 3) { // cubic became line or quad
414 for (int index = 0; index < order; ++index) {
415 SkPoint* pt = reducePts.append();
416 pt->fX = SkDoubleToScalar(dst[index].x);
417 pt->fY = SkDoubleToScalar(dst[index].y);
418 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000419 }
420 return (SkPath::Verb) (order - 1);
421}
422
caryclark@google.com15fa1382012-05-07 20:49:36 +0000423static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000424 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000425 return isLinear(aQuad, 0, 2);
426}
427
428static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000429 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000430 return isLinear(aCubic, 0, 3);
431}
432
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000433static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000434 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000435 double x[2];
436 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000437 xy_at_t(aLine, endT, x[1], *(double*) 0);
438 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000439}
440
441static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000442 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000443 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000444}
445
446static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000447 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000448 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000449}
450
451static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
452 NULL,
453 LineLeftMost,
454 QuadLeftMost,
455 CubicLeftMost
456};
457
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000458class Segment;
459
caryclark@google.com15fa1382012-05-07 20:49:36 +0000460// sorting angles
461// given angles of {dx dy ddx ddy dddx dddy} sort them
462class Angle {
463public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000464 // FIXME: this is bogus for quads and cubics
465 // if the quads and cubics' line from end pt to ctrl pt are coincident,
466 // there's no obvious way to determine the curve ordering from the
467 // derivatives alone. In particular, if one quadratic's coincident tangent
468 // is longer than the other curve, the final control point can place the
469 // longer curve on either side of the shorter one.
470 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
471 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000472
caryclark@google.com32546db2012-08-31 20:55:07 +0000473 // start here
474 /*(
475 for quads and cubics, set up a parameterized line (e.g. LineParameters )
476 for points [0] to [1]. See if point [2] is on that line, or on one side
477 or the other. If it both quads' end points are on the same side, choose
478 the shorter tangent. If the tangents are equal, choose the better second
479 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000480
caryclark@google.com32546db2012-08-31 20:55:07 +0000481 maybe I set up LineParameters lazily
482 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000483 bool operator<(const Angle& rh) const {
caryclark@google.com32546db2012-08-31 20:55:07 +0000484 if ((fDy < 0) ^ (rh.fDy < 0)) { // OPTIMIZATION: better to use fDy * rh.fDy < 0 ?
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000485 return fDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000486 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000487 if (fDy == 0 && rh.fDy == 0 && fDx * rh.fDx < 0) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000488 return fDx < rh.fDx;
489 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000490 AngleValue cmp = fDx * rh.fDy - rh.fDx * fDy;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000491 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000492 return cmp < 0;
493 }
caryclark@google.com32546db2012-08-31 20:55:07 +0000494 // at this point, the initial tangent line is coincident
495 #if !HIGH_DEF_ANGLES // old way
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000496 AngleValue dy = approximately_pin(fDy + fDDy);
497 AngleValue rdy = approximately_pin(rh.fDy + rh.fDDy);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000498 if (dy * rdy < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000499 return dy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000500 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000501 AngleValue dx = approximately_pin(fDx + fDDx);
502 AngleValue rdx = approximately_pin(rh.fDx + rh.fDDx);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000503 if (dy == 0 && rdy == 0 && dx * rdx < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000504 return dx < rdx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000505 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000506 cmp = dx * rdy - rdx * dy;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000507 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000508 return cmp < 0;
509 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000510 dy = approximately_pin(dy + fDDDy);
511 rdy = approximately_pin(rdy + rh.fDDDy);
512 if (dy * rdy < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000513 return dy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000514 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000515 dx = approximately_pin(dx + fDDDx);
516 rdx = approximately_pin(rdx + rh.fDDDx);
517 if (dy == 0 && rdy == 0 && dx * rdx < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000518 return dx < rdx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000519 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000520 return dx * rdy < rdx * dy;
caryclark@google.com32546db2012-08-31 20:55:07 +0000521 #else // new way
522 if (fSide * rh.fSide <= 0) {
523 SkASSERT(fSide != rh.fSide);
524 return fSide < rh.fSide;
525 }
526 if (fDy != rh.fDy) {
527 return fabs(fDy) < fabs(rh.fDy);
528 }
529 if (fDx != rh.fDx) {
530 return fabs(fDx) < fabs(rh.fDx);
531 }
532 if (fSide != rh.fSide) {
533 return fSide < rh.fSide;
534 }
535 SkASSERT(0); // add code for cubic case
536 #endif
caryclark@google.com15fa1382012-05-07 20:49:36 +0000537 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000538
caryclark@google.com47580692012-07-23 12:14:49 +0000539 double dx() const {
540 return fDx;
541 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000542
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000543 double dy() const {
544 return fDy;
545 }
546
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000547 int end() const {
548 return fEnd;
549 }
550
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000551 bool isHorizontal() const {
552 return fDy == 0 && fDDy == 0 && fDDDy == 0;
553 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000554
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000555 // high precision version
556#if HIGH_DEF_ANGLES
557 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
558 int start, int end, double startT, double endT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000559 fSegment = segment;
560 fStart = start;
561 fEnd = end;
caryclark@google.com32546db2012-08-31 20:55:07 +0000562 switch (verb) {
563 case SkPath::kLine_Verb:
564 _Line l;
565 LineSubDivideHD(orig, startT, endT, l);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000566 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000567 fSide = 0;
568 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
569 fTangent1.lineEndPoints(l);
570 break;
571 case SkPath::kQuad_Verb:
572 Quadratic q;
573 QuadSubDivideHD(orig, startT, endT, q);
574 fDDx = approximately_pin(q[2].x - 2 * q[1].x + q[0].x);
575 fDDy = approximately_pin(q[2].y - 2 * q[1].y + q[0].y);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000576 fDDDx = fDDDy = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000577 fTangent1.quadEndPoints(q, 0, 1);
578 fSide = -fTangent1.pointDistance(q[2]);
579 break;
580 case SkPath::kCubic_Verb:
581 Cubic c;
582 CubicSubDivideHD(orig, startT, endT, c);
583 fDDx = approximately_pin(c[2].x - 2 * c[1].x + c[0].x);
584 fDDy = approximately_pin(c[2].y - 2 * c[1].y + c[0].y);
585 fDDDx = approximately_pin(c[3].x + 3 * (c[1].x - c[2].x) - c[0].x);
586 fDDDy = approximately_pin(c[3].y + 3 * (c[1].y - c[2].y) - c[0].y);
587 fTangent1.cubicEndPoints(c, 0, 1);
588 fSide = -fTangent1.pointDistance(c[2]);
589 break;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000590 }
caryclark@google.com32546db2012-08-31 20:55:07 +0000591 // OPTIMIZATION: don't need these, access fTangent1 directly
592 fDx = fTangent1.dx();
593 fDy = fTangent1.dy();
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000594 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000595
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000596#else
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000597 // since all angles share a point, this needs to know which point
598 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
599 // practically, this should only be called by addAngle
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000600 void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000601 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000602 SkASSERT(start != end);
603 fSegment = segment;
604 fStart = start;
605 fEnd = end;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000606 fDx = approximately_pin(pts[1].fX - pts[0].fX); // b - a
607 fDy = approximately_pin(pts[1].fY - pts[0].fY);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000608 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000609 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000610 return;
611 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000612 fDDx = approximately_pin(pts[2].fX - pts[1].fX - fDx); // a - 2b + c
613 fDDy = approximately_pin(pts[2].fY - pts[1].fY - fDy);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000614 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000615 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000616 return;
617 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000618 fDDDx = approximately_pin(pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX);
619 fDDDy = approximately_pin(pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000620 }
621
622 // noncoincident quads/cubics may have the same initial angle
623 // as lines, so must sort by derivatives as well
624 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000625 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000626 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000627 fSegment = segment;
628 fStart = start;
629 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000630 fDx = pts[1].fX - pts[0].fX; // b - a
631 fDy = pts[1].fY - pts[0].fY;
632 if (verb == SkPath::kLine_Verb) {
633 fDDx = fDDy = fDDDx = fDDDy = 0;
634 return;
635 }
636 if (verb == SkPath::kQuad_Verb) {
637 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
638 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
639 int larger = std::max(abs(uplsX), abs(uplsY));
640 int shift = 0;
641 double flatT;
642 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
643 LineParameters implicitLine;
caryclark@google.com32546db2012-08-31 20:55:07 +0000644 MAKE_CONST_LINE(tangent, pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000645 implicitLine.lineEndPoints(tangent);
646 implicitLine.normalize();
647 while (larger > UlpsEpsilon * 1024) {
648 larger >>= 2;
649 ++shift;
650 flatT = 0.5 / (1 << shift);
651 QuadXYAtT(pts, flatT, &ddPt);
652 _Point _pt = {ddPt.fX, ddPt.fY};
653 double distance = implicitLine.pointDistance(_pt);
654 if (approximately_zero(distance)) {
655 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
656 break;
657 }
658 }
659 flatT = 0.5 / (1 << shift);
660 QuadXYAtT(pts, flatT, &ddPt);
661 fDDx = ddPt.fX - pts[0].fX;
662 fDDy = ddPt.fY - pts[0].fY;
663 SkASSERT(fDDx != 0 || fDDy != 0);
664 fDDDx = fDDDy = 0;
665 return;
666 }
667 SkASSERT(0); // FIXME: add cubic case
668 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000669#endif
rmistry@google.comd6176b02012-08-23 18:14:13 +0000670
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000671 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000672 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000673 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000674
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000675 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000676 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000677 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000678
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000679 int start() const {
680 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000681 }
682
caryclark@google.comc899ad92012-08-23 15:24:42 +0000683#if DEBUG_ANGLE
684 void debugShow(const SkPoint& a) const {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000685 SkDebugf(" d=(%1.9g,%1.9g) dd=(%1.9g,%1.9g) ddd=(%1.9g,%1.9g)\n",
caryclark@google.comc899ad92012-08-23 15:24:42 +0000686 fDx, fDy, fDDx, fDDy, fDDDx, fDDDy);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000687 AngleValue ax = (AngleValue) a.fX;
688 AngleValue ay = (AngleValue) a.fY;
689 AngleValue bx, by, cx, cy, dx, dy;
690 bx = ax + fDx; // add b - a
691 by = ay + fDy;
692 cx = ax + 2 * fDx + fDDx; // add a + 2(b - a) to a - 2b + c
693 cy = ay + 2 * fDy + fDDy;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000694 if (fDDDx == 0 && fDDDy == 0) {
695 if (fDDx == 0 && fDDy == 0) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000696 SkDebugf(
697" {SkPath::kLine_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g} }},\n",
698 ax, ay, bx, by);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000699 } else {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000700 SkDebugf(
701" {SkPath::kQuad_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}}},\n",
702 ax, ay, bx, by, cx, cy);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000703 }
704 } else {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000705 dx = fDDDx - ax - 3 * (cx - bx);
706 dy = fDDDy - ay - 3 * (cy - by);
707 SkDebugf(
708" {SkPath::kCubic_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}}},\n",
709 ax, ay, bx, by, cx, cy, dx, dy);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000710 }
711 }
712#endif
713
caryclark@google.com15fa1382012-05-07 20:49:36 +0000714private:
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000715 AngleValue fDx;
716 AngleValue fDy;
717 AngleValue fDDx;
718 AngleValue fDDy;
719 AngleValue fDDDx;
720 AngleValue fDDDy;
caryclark@google.com32546db2012-08-31 20:55:07 +0000721 double fSide;
722 LineParameters fTangent1; // FIXME: replaces fDx, fDy
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000723 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000724 int fStart;
725 int fEnd;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000726};
727
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000728static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
729 int angleCount = angles.count();
730 int angleIndex;
731 angleList.setReserve(angleCount);
732 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
733 *angleList.append() = &angles[angleIndex];
734 }
735 QSort<Angle>(angleList.begin(), angleList.end() - 1);
736}
737
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000738// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000739struct Bounds : public SkRect {
740 static bool Intersects(const Bounds& a, const Bounds& b) {
741 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
742 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
743 }
744
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000745 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
746 if (left < fLeft) {
747 fLeft = left;
748 }
749 if (top < fTop) {
750 fTop = top;
751 }
752 if (right > fRight) {
753 fRight = right;
754 }
755 if (bottom > fBottom) {
756 fBottom = bottom;
757 }
758 }
759
760 void add(const Bounds& toAdd) {
761 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
762 }
763
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000764 bool isEmpty() {
765 return fLeft > fRight || fTop > fBottom
766 || fLeft == fRight && fTop == fBottom
767 || isnan(fLeft) || isnan(fRight)
768 || isnan(fTop) || isnan(fBottom);
769 }
770
771 void setCubicBounds(const SkPoint a[4]) {
772 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000773 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000774 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000775 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
776 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000777 }
778
779 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000780 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000781 _Rect dRect;
782 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000783 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
784 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000785 }
786};
787
caryclark@google.com2ddff932012-08-07 21:25:27 +0000788static bool useInnerWinding(int outerWinding, int innerWinding) {
789 SkASSERT(outerWinding != innerWinding);
790 int absOut = abs(outerWinding);
791 int absIn = abs(innerWinding);
792 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
793 if (outerWinding * innerWinding < 0) {
794#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000795 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000796 outerWinding, innerWinding, result ? "true" : "false");
797#endif
798 }
799 return result;
800}
801
caryclark@google.com15fa1382012-05-07 20:49:36 +0000802struct Span {
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000803 Segment* fOther;
caryclark@google.com27c449a2012-07-27 18:26:38 +0000804 mutable SkPoint fPt; // lazily computed as needed
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000805 double fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000806 double fOtherT; // value at fOther[fOtherIndex].fT
807 int fOtherIndex; // can't be used during intersection
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000808 int fWindSum; // accumulated from contours surrounding this one
809 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000810 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000811};
812
813class Segment {
814public:
815 Segment() {
816#if DEBUG_DUMP
817 fID = ++gSegmentID;
818#endif
819 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000820
caryclark@google.com9764cc62012-07-12 19:29:45 +0000821 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
822 if (activeAngleInner(index, done, angles)) {
823 return true;
824 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000825 double referenceT = fTs[index].fT;
826 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000827 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000828 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000829 return true;
830 }
831 }
832 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000833 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000834 return true;
835 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000836 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000837 return false;
838 }
839
caryclark@google.com9764cc62012-07-12 19:29:45 +0000840 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000841 Span* span = &fTs[index];
842 Segment* other = span->fOther;
843 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +0000844 return other->activeAngleInner(oIndex, done, angles);
845 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000846
caryclark@google.com9764cc62012-07-12 19:29:45 +0000847 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
848 int next = nextSpan(index, 1);
849 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000850 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000851 if (upSpan.fWindValue) {
852 addAngle(angles, index, next);
853 if (upSpan.fDone) {
854 done++;
855 } else if (upSpan.fWindSum != SK_MinS32) {
856 return true;
857 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000858 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000859 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000860 int prev = nextSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000861 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +0000862 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000863 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000864 if (downSpan.fWindValue) {
865 addAngle(angles, index, prev);
866 if (downSpan.fDone) {
867 done++;
868 } else if (downSpan.fWindSum != SK_MinS32) {
869 return true;
870 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000871 }
872 }
873 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000874 }
875
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000876 SkScalar activeTop() const {
877 SkASSERT(!done());
878 int count = fTs.count();
879 SkScalar result = SK_ScalarMax;
880 bool lastDone = true;
881 for (int index = 0; index < count; ++index) {
882 bool done = fTs[index].fDone;
883 if (!done || !lastDone) {
884 SkScalar y = yAtT(index);
885 if (result > y) {
886 result = y;
887 }
888 }
889 lastDone = done;
890 }
891 SkASSERT(result < SK_ScalarMax);
892 return result;
893 }
894
895 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000896 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000897 Angle* angle = angles.append();
898#if HIGH_DEF_ANGLES==0 // old way
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000899 SkPoint edge[4];
900 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000901 angle->set(edge, fVerb, this, start, end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000902#else // new way : compute temp edge in higher precision
903 angle->set(fPts, fVerb, this, start, end, fTs[start].fT, fTs[end].fT);
904#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000905 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000906
caryclark@google.com2ddff932012-08-07 21:25:27 +0000907 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +0000908 double oEnd) {
909 int tIndex = -1;
910 int tCount = fTs.count();
911 int oIndex = -1;
912 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +0000913 do {
914 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000915 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +0000916 int tIndexStart = tIndex;
917 do {
918 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000919 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +0000920 int oIndexStart = oIndex;
921 double nextT;
922 do {
923 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000924 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000925 double oNextT;
926 do {
927 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000928 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000929 // at this point, spans before and after are at:
930 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
931 // if tIndexStart == 0, no prior span
932 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +0000933
caryclark@google.comcc905052012-07-25 20:59:42 +0000934 // advance the span with zero winding
935 // if the following span exists (not past the end, non-zero winding)
936 // connect the two edges
937 if (!fTs[tIndexStart].fWindValue) {
938 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
939 #if DEBUG_CONCIDENT
940 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
941 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000942 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
943 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +0000944 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000945 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000946 }
947 if (nextT < 1 && fTs[tIndex].fWindValue) {
948 #if DEBUG_CONCIDENT
949 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
950 __FUNCTION__, fID, other.fID, tIndex,
951 fTs[tIndex].fT, xyAtT(tIndex).fX,
952 xyAtT(tIndex).fY);
953 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000954 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000955 }
956 } else {
957 SkASSERT(!other.fTs[oIndexStart].fWindValue);
958 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
959 #if DEBUG_CONCIDENT
960 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
961 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000962 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
963 other.xyAtT(oIndexStart).fY);
964 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +0000965 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +0000966 }
967 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
968 #if DEBUG_CONCIDENT
969 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
970 __FUNCTION__, fID, other.fID, oIndex,
971 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
972 other.xyAtT(oIndex).fY);
973 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
974 #endif
975 }
976 }
977 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000978
caryclark@google.comcc905052012-07-25 20:59:42 +0000979 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
980 double oEnd) {
981 // walk this to outsideTs[0]
982 // walk other to outsideTs[1]
983 // if either is > 0, add a pointer to the other, copying adjacent winding
984 int tIndex = -1;
985 int oIndex = -1;
986 double tStart = outsideTs[0];
987 double oStart = outsideTs[1];
988 do {
989 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000990 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +0000991 do {
992 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000993 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +0000994 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +0000995 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000996 }
997 tStart = fTs[tIndex].fT;
998 oStart = other.fTs[oIndex].fT;
999 do {
1000 double nextT;
1001 do {
1002 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001003 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001004 tStart = nextT;
1005 do {
1006 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001007 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001008 oStart = nextT;
1009 if (tStart == 1 && oStart == 1) {
1010 break;
1011 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001012 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001013 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001014 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001015
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001016 void addCubic(const SkPoint pts[4]) {
1017 init(pts, SkPath::kCubic_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001018 fBounds.setCubicBounds(pts);
1019 }
1020
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001021 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001022 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001023 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001024 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001025 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001026 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001027 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001028 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
1029 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
1030 if (fVerb > 1) {
1031 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
1032 }
1033 if (fVerb > 2) {
1034 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
1035 }
1036 SkDebugf("\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001037 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001038 switch (fVerb) {
1039 case SkPath::kLine_Verb:
1040 path.lineTo(edge[1].fX, edge[1].fY);
1041 break;
1042 case SkPath::kQuad_Verb:
1043 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
1044 break;
1045 case SkPath::kCubic_Verb:
1046 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
1047 edge[3].fX, edge[3].fY);
1048 break;
1049 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001050 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001051 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001052 }
1053
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001054 void addLine(const SkPoint pts[2]) {
1055 init(pts, SkPath::kLine_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001056 fBounds.set(pts, 2);
1057 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001058
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001059 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
1060 const SkPoint& pt = xyAtT(tIndex);
1061 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001062 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001063 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001064 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001065 path.moveTo(pt.fX, pt.fY);
1066 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001067 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001068 }
1069
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001070 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001071 void addOtherT(int index, double otherT, int otherIndex) {
1072 Span& span = fTs[index];
1073 span.fOtherT = otherT;
1074 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001075 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001076
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001077 void addQuad(const SkPoint pts[3]) {
1078 init(pts, SkPath::kQuad_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001079 fBounds.setQuadBounds(pts);
1080 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001081
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001082 // Defer all coincident edge processing until
1083 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001084
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001085// no need to be tricky; insert in normal T order
1086// resolve overlapping ts when considering coincidence later
1087
1088 // add non-coincident intersection. Resulting edges are sorted in T.
1089 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001090 // FIXME: in the pathological case where there is a ton of intercepts,
1091 // binary search?
1092 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001093 size_t tCount = fTs.count();
caryclark@google.comc899ad92012-08-23 15:24:42 +00001094 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001095 if (approximately_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001096 newT = 0;
1097 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001098 if (approximately_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001099 newT = 1;
1100 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001101 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001102 // OPTIMIZATION: if there are three or more identical Ts, then
1103 // the fourth and following could be further insertion-sorted so
1104 // that all the edges are clockwise or counterclockwise.
1105 // This could later limit segment tests to the two adjacent
1106 // neighbors, although it doesn't help with determining which
1107 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001108 if (newT < fTs[index].fT) {
1109 insertedAt = index;
1110 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001111 }
1112 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001113 Span* span;
1114 if (insertedAt >= 0) {
1115 span = fTs.insert(insertedAt);
1116 } else {
1117 insertedAt = tCount;
1118 span = fTs.append();
1119 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001120 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001121 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001122 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001123 span->fWindSum = SK_MinS32;
1124 span->fWindValue = 1;
1125 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001126 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001127 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001128 return insertedAt;
1129 }
1130
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001131 // set spans from start to end to decrement by one
1132 // note this walks other backwards
1133 // FIMXE: there's probably an edge case that can be constructed where
1134 // two span in one segment are separated by float epsilon on one span but
1135 // not the other, if one segment is very small. For this
1136 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001137 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001138 // sorted? It feels better in such a case to match the span's other span
1139 // pointer since both coincident segments must contain the same spans.
1140 void addTCancel(double startT, double endT, Segment& other,
1141 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001142 SkASSERT(!approximately_negative(endT - startT));
1143 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001144 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001145 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001146 ++index;
1147 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001148 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001149 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001150 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001151 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001152 Span* test = &fTs[index];
1153 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001154 SkTDArray<double> outsideTs;
1155 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001156 do {
1157 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001158 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001159 double testT = test->fT;
1160 double oTestT = oTest->fT;
1161 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001162 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001163 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001164 decrementSpan(span);
1165 } else if (track && span->fT < 1 && oTestT < 1) {
1166 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001167 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001168 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001169 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001170 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001171 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1172 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1173 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001174 while (approximately_negative(otherTMatchStart - oSpan->fT)
1175 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001176 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001177 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001178 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001179 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001180 other.decrementSpan(oSpan);
1181 } else if (track && oSpan->fT < 1 && testT < 1) {
1182 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001183 }
1184 if (!oIndex) {
1185 break;
1186 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001187 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001188 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001189 test = span;
1190 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001191 } while (!approximately_negative(endT - test->fT));
1192 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001193 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001194 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001195 double tStart = outsideTs[0];
1196 double oStart = outsideTs[1];
1197 addCancelOutsides(tStart, oStart, other, oEndT);
1198 int count = outsideTs.count();
1199 if (count > 2) {
1200 double tStart = outsideTs[count - 2];
1201 double oStart = outsideTs[count - 1];
1202 addCancelOutsides(tStart, oStart, other, oEndT);
1203 }
caryclark@google.com18063442012-07-25 12:05:18 +00001204 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001205 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001206 double tStart = oOutsideTs[0];
1207 double oStart = oOutsideTs[1];
1208 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001209 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001210 }
1211
1212 // set spans from start to end to increment the greater by one and decrement
1213 // the lesser
caryclark@google.com24bec792012-08-20 12:43:57 +00001214 void addTCoincident(const int xorMask, double startT, double endT, Segment& other,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001215 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001216 SkASSERT(!approximately_negative(endT - startT));
1217 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001218 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001219 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001220 ++index;
1221 }
1222 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001223 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001224 ++oIndex;
1225 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001226 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001227 Span* test = &fTs[index];
1228 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001229 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001230 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001231 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001232 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001233 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001234 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com24bec792012-08-20 12:43:57 +00001235 bool winding = xorMask < 0;
1236 bool decrementThis = (test->fWindValue < oTest->fWindValue) & winding;
1237 bool decrementOther = (test->fWindValue >= oTest->fWindValue) & winding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001238 Span* end = test;
1239 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001240 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001241 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001242 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001243 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001244 if (transfer) {
1245 if (decrementOther) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001246 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001247 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001248 #endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001249 ++(end->fWindValue);
caryclark@google.com18063442012-07-25 12:05:18 +00001250 } else if (decrementSpan(end)) {
1251 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001252 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001253 } else if (oTest->fWindValue) {
1254 SkASSERT(!decrementOther);
1255 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1256 TrackOutside(xOutsideTs, end->fT, oStartT);
1257 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001258 }
1259 end = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001260 } while (approximately_negative(end->fT - test->fT));
caryclark@google.com59823f72012-08-09 18:17:47 +00001261 // because of the order in which coincidences are resolved, this and other
1262 // may not have the same intermediate points. Compute the corresponding
1263 // intermediate T values (using this as the master, other as the follower)
1264 // and walk other conditionally -- hoping that it catches up in the end
1265 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001266 Span* oEnd = oTest;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001267 while (!approximately_negative(oEndT - oEnd->fT) && approximately_negative(oEnd->fT - otherTMatch)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00001268 if (transfer) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001269 if (decrementThis) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001270 #ifdef SK_DEBUG
1271 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1272 #endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001273 ++(oEnd->fWindValue);
caryclark@google.com18063442012-07-25 12:05:18 +00001274 } else if (other.decrementSpan(oEnd)) {
1275 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001276 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001277 } else if (test->fWindValue) {
1278 SkASSERT(!decrementOther);
1279 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1280 SkASSERT(0); // track for later?
1281 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001282 }
1283 oEnd = &other.fTs[++oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001284 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001285 test = end;
1286 oTest = oEnd;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001287 } while (!approximately_negative(endT - test->fT));
1288 SkASSERT(approximately_negative(oTest->fT - oEndT));
1289 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001290 if (!done()) {
1291 if (outsideTs.count()) {
1292 addCoinOutsides(outsideTs, other, oEndT);
1293 }
1294 if (xOutsideTs.count()) {
1295 addCoinOutsides(xOutsideTs, other, oEndT);
1296 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001297 }
1298 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001299 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001300 }
1301 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001302
caryclark@google.comcc905052012-07-25 20:59:42 +00001303 // FIXME: this doesn't prevent the same span from being added twice
1304 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001305 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001306 int tCount = fTs.count();
1307 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1308 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001309 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001310 break;
1311 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001312 if (approximately_negative(span.fT - t) && span.fOther == &other && span.fOtherT == otherT) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001313#if DEBUG_ADD_T_PAIR
1314 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1315 __FUNCTION__, fID, t, other.fID, otherT);
1316#endif
1317 return;
1318 }
1319 }
caryclark@google.com47580692012-07-23 12:14:49 +00001320#if DEBUG_ADD_T_PAIR
1321 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1322 __FUNCTION__, fID, t, other.fID, otherT);
1323#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001324 int insertedAt = addT(t, &other);
1325 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001326 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001327 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001328 matchWindingValue(insertedAt, t, borrowWind);
1329 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001330 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001331
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001332 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001333 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001334 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1335 addAngle(angles, end, start);
1336 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001337 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001338 int step = SkSign32(end - start);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001339 int tIndex = nextSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001340 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001341 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001342 }
1343 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001344
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001345 const Bounds& bounds() const {
1346 return fBounds;
1347 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001348
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001349 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001350 double referenceT = fTs[index].fT;
1351 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001352 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001353 buildAnglesInner(lesser, angles);
1354 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001355 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001356 buildAnglesInner(index, angles);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001357 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001358 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001359
1360 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1361 Span* span = &fTs[index];
1362 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001363 // if there is only one live crossing, and no coincidence, continue
1364 // in the same direction
1365 // if there is coincidence, the only choice may be to reverse direction
1366 // find edge on either side of intersection
1367 int oIndex = span->fOtherIndex;
1368 // if done == -1, prior span has already been processed
1369 int step = 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001370 int next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001371 if (next < 0) {
1372 step = -step;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001373 next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001374 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001375 // add candidate into and away from junction
1376 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001377 }
1378
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001379 // figure out if the segment's ascending T goes clockwise or not
1380 // not enough context to write this as shown
1381 // instead, add all segments meeting at the top
1382 // sort them using buildAngleList
1383 // find the first in the sort
1384 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001385 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001386 SkASSERT(0); // incomplete
1387 return false;
1388 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001389
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001390 int computeSum(int startIndex, int endIndex) {
1391 SkTDArray<Angle> angles;
1392 addTwoAngles(startIndex, endIndex, angles);
1393 buildAngles(endIndex, angles);
1394 SkTDArray<Angle*> sorted;
1395 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001396#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00001397 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001398#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001399 int angleCount = angles.count();
1400 const Angle* angle;
1401 const Segment* base;
1402 int winding;
1403 int firstIndex = 0;
1404 do {
1405 angle = sorted[firstIndex];
1406 base = angle->segment();
1407 winding = base->windSum(angle);
1408 if (winding != SK_MinS32) {
1409 break;
1410 }
1411 if (++firstIndex == angleCount) {
1412 return SK_MinS32;
1413 }
1414 } while (true);
1415 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001416 int spanWinding = base->spanSign(angle);
1417 bool inner = useInnerWinding(winding + spanWinding, winding);
1418 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001419 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001420 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001421 inner ? winding + spanWinding : winding);
1422 #endif
1423 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001424 winding += spanWinding;
1425 }
1426 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001427 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001428 #endif
1429 int nextIndex = firstIndex + 1;
1430 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001431 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001432 do {
1433 if (nextIndex == angleCount) {
1434 nextIndex = 0;
1435 }
1436 angle = sorted[nextIndex];
1437 Segment* segment = angle->segment();
1438 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001439 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001440 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001441 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001442 maxWinding = winding;
1443 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001444 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001445 }
1446 } while (++nextIndex != lastIndex);
1447 return windSum(SkMin32(startIndex, endIndex));
1448 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001449
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001450 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001451 int bestT = -1;
1452 SkScalar top = bounds().fTop;
1453 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001454 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001455 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001456 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001457 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001458 if (fTs[start].fWindValue == 0) {
1459 continue;
1460 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001461 SkPoint edge[4];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001462 // OPTIMIZE: wrap this so that if start==0 end==fTCount-1 we can
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001463 // work with the original data directly
caryclark@google.com24bec792012-08-20 12:43:57 +00001464 double startT = fTs[start].fT;
1465 double endT = fTs[end].fT;
1466 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001467 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001468 Intersections intersections;
1469 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1470 false, intersections);
1471 if (pts == 0) {
1472 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001473 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001474 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1475 // if the intersection is edge on, wait for another one
1476 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001477 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001478 SkASSERT(pts == 1); // FIXME: more code required to disambiguate
1479 SkPoint pt;
1480 double foundT = intersections.fT[0][0];
caryclark@google.com24bec792012-08-20 12:43:57 +00001481 double testT = startT + (endT - startT) * foundT;
1482 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
caryclark@google.com59823f72012-08-09 18:17:47 +00001483 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001484 bestY = pt.fY;
1485 bestT = foundT < 1 ? start : end;
caryclark@google.com24bec792012-08-20 12:43:57 +00001486 hitT = testT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001487 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001488 } while (fTs[end].fT != 1);
1489 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001490 }
caryclark@google.com18063442012-07-25 12:05:18 +00001491
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001492 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1493 // if a segment is connected to this one, consider it crossing
1494 int tIndex;
1495 if (fPts[0].fX == basePt.fX) {
1496 tIndex = 0;
1497 do {
1498 const Span& sSpan = fTs[tIndex];
1499 const Segment* sOther = sSpan.fOther;
1500 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1501 continue;
1502 }
1503 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1504 : sOther->fBounds.fRight > basePt.fX) {
1505 return true;
1506 }
1507 } while (fTs[++tIndex].fT == 0);
1508 }
1509 if (fPts[fVerb].fX == basePt.fX) {
1510 tIndex = fTs.count() - 1;
1511 do {
1512 const Span& eSpan = fTs[tIndex];
1513 const Segment* eOther = eSpan.fOther;
1514 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1515 continue;
1516 }
1517 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1518 : eOther->fBounds.fRight > basePt.fX) {
1519 return true;
1520 }
1521 } while (fTs[--tIndex].fT == 1);
1522 }
1523 return false;
1524 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001525
caryclark@google.com18063442012-07-25 12:05:18 +00001526 bool decrementSpan(Span* span) {
1527 SkASSERT(span->fWindValue > 0);
1528 if (--(span->fWindValue) == 0) {
1529 span->fDone = true;
1530 ++fDoneSpans;
1531 return true;
1532 }
1533 return false;
1534 }
1535
caryclark@google.com15fa1382012-05-07 20:49:36 +00001536 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001537 SkASSERT(fDoneSpans <= fTs.count());
1538 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001539 }
1540
caryclark@google.com47580692012-07-23 12:14:49 +00001541 bool done(const Angle& angle) const {
1542 int start = angle.start();
1543 int end = angle.end();
1544 const Span& mSpan = fTs[SkMin32(start, end)];
1545 return mSpan.fDone;
1546 }
1547
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001548 // so the span needs to contain the pairing info found here
1549 // this should include the winding computed for the edge, and
1550 // what edge it connects to, and whether it is discarded
1551 // (maybe discarded == abs(winding) > 1) ?
1552 // only need derivatives for duration of sorting, add a new struct
1553 // for pairings, remove extra spans that have zero length and
1554 // reference an unused other
1555 // for coincident, the last span on the other may be marked done
1556 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001557
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001558 // if loop is exhausted, contour may be closed.
1559 // FIXME: pass in close point so we can check for closure
1560
1561 // given a segment, and a sense of where 'inside' is, return the next
1562 // segment. If this segment has an intersection, or ends in multiple
1563 // segments, find the mate that continues the outside.
1564 // note that if there are multiples, but no coincidence, we can limit
1565 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00001566
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001567 // mark found segments as done
1568
caryclark@google.com15fa1382012-05-07 20:49:36 +00001569 // start is the index of the beginning T of this edge
1570 // it is guaranteed to have an end which describes a non-zero length (?)
1571 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00001572 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
1573 int& nextStart, int& nextEnd, int& winding, int& spanWinding) {
1574 const int startIndex = nextStart;
1575 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001576 int outerWinding = winding;
1577 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00001578 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001579 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1580 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00001581 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001582 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001583 outerWinding = innerWinding;
1584 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001585 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001586 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001587 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1588 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001589 int step = SkSign32(endIndex - startIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001590 int end = nextSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001591 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001592 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001593 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001594 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001595 // mark the smaller of startIndex, endIndex done, and all adjacent
1596 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001597 #if DEBUG_WINDING
1598 SkDebugf("%s simple\n", __FUNCTION__);
1599 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001600 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001601 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001602 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00001603 double startT = other->fTs[nextStart].fT;
1604 nextEnd = nextStart;
1605 do {
1606 nextEnd += step;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001607 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00001608 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001609 return other;
1610 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001611 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001612 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001613 SkASSERT(startIndex - endIndex != 0);
1614 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001615 addTwoAngles(startIndex, end, angles);
1616 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001617 SkTDArray<Angle*> sorted;
1618 sortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001619 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001620 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001621 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00001622 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001623 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001624 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001625 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001626 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001627 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00001628 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001629 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001630 int nextIndex = firstIndex + 1;
1631 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1632 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00001633 // FIXME: found done logic probably fails if there are more than 4
1634 // sorted angles. It should bias towards the first and last undone
1635 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001636 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00001637 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00001638 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001639 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00001640 bool altFlipped = false;
1641 bool foundFlipped = false;
1642 int foundMax = SK_MinS32;
1643 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001644 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00001645 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001646 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001647 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001648 nextIndex = 0;
1649 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001650 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00001651 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00001652 if (sumWinding) {
1653 lastNonZeroSum = sumWinding;
1654 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00001655 nextSegment = nextAngle->segment();
caryclark@google.com2ddff932012-08-07 21:25:27 +00001656 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00001657 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001658 #if DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00001659 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00001660 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
1661 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001662 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00001663 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00001664 if (!active) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001665 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001666 // FIXME: seems like a bug that this isn't calling userInnerWinding
caryclark@google.com47580692012-07-23 12:14:49 +00001667 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00001668 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001669 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001670 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001671 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00001672 return NULL;
1673 }
caryclark@google.com47580692012-07-23 12:14:49 +00001674 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001675 foundAngle = nextAngle;
caryclark@google.com47580692012-07-23 12:14:49 +00001676 foundDone = nextSegment->done(*nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00001677 foundFlipped = altFlipped;
1678 foundMax = maxWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001679 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001680 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001681 }
caryclark@google.com24bec792012-08-20 12:43:57 +00001682 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00001683 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001684 if (foundAngle && foundDone2) {
1685 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001686 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00001687 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00001688 foundAngle = nextAngle;
caryclark@google.com24bec792012-08-20 12:43:57 +00001689 foundDone2 = nextSegment->done(*nextAngle);
1690 foundFlipped = altFlipped;
1691 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00001692 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001693 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001694 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001695 }
1696 // if the winding is non-zero, nextAngle does not connect to
1697 // current chain. If we haven't done so already, mark the angle
1698 // as done, record the winding value, and mark connected unambiguous
1699 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001700 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001701 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00001702 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001703 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001704 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001705 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001706 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001707 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00001708 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001709 }
1710 if (last) {
1711 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001712 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001713 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001714 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00001715 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001716 if (!foundAngle) {
1717 return NULL;
1718 }
1719 nextStart = foundAngle->start();
1720 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00001721 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00001722 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001723 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
1724 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00001725 if (winding) {
1726 #if DEBUG_WINDING
1727 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
1728 if (foundSum == SK_MinS32) {
1729 SkDebugf("?");
1730 } else {
1731 SkDebugf("%d", foundSum);
1732 }
1733 SkDebugf(" foundMax=");
1734 if (foundMax == SK_MinS32) {
1735 SkDebugf("?");
1736 } else {
1737 SkDebugf("%d", foundMax);
1738 }
1739 SkDebugf("\n");
1740 #endif
1741 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001742 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00001743 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001744 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00001745 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00001746 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001747 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001748
caryclark@google.com24bec792012-08-20 12:43:57 +00001749 Segment* findNextXor(int& nextStart, int& nextEnd) {
1750 const int startIndex = nextStart;
1751 const int endIndex = nextEnd;
1752 SkASSERT(startIndex != endIndex);
1753 int count = fTs.count();
1754 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1755 : startIndex > 0);
1756 int step = SkSign32(endIndex - startIndex);
1757 int end = nextSpan(startIndex, step);
1758 SkASSERT(end >= 0);
1759 Span* endSpan = &fTs[end];
1760 Segment* other;
1761 markDone(SkMin32(startIndex, endIndex), 1);
1762 if (isSimple(end)) {
1763 #if DEBUG_WINDING
1764 SkDebugf("%s simple\n", __FUNCTION__);
1765 #endif
1766 other = endSpan->fOther;
1767 nextStart = endSpan->fOtherIndex;
1768 double startT = other->fTs[nextStart].fT;
1769 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001770 if ((approximately_less_than_zero(startT) && step < 0)
1771 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001772 step = -step;
1773 SkDEBUGCODE(firstLoop = false;)
1774 }
1775 do {
1776 nextEnd = nextStart;
1777 do {
1778 nextEnd += step;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001779 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00001780 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
1781 break;
1782 }
caryclark@google.com03f97062012-08-21 13:13:52 +00001783 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00001784 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00001785 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00001786 SkDEBUGCODE(firstLoop = false;)
1787 step = -step;
1788 } while (true);
1789 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1790 return other;
1791 }
1792 SkTDArray<Angle> angles;
1793 SkASSERT(startIndex - endIndex != 0);
1794 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1795 addTwoAngles(startIndex, end, angles);
1796 buildAngles(end, angles);
1797 SkTDArray<Angle*> sorted;
1798 sortAngles(angles, sorted);
1799 int angleCount = angles.count();
1800 int firstIndex = findStartingEdge(sorted, startIndex, end);
1801 SkASSERT(firstIndex >= 0);
1802 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001803 debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00001804 #endif
1805 SkASSERT(sorted[firstIndex]->segment() == this);
1806 int nextIndex = firstIndex + 1;
1807 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1808 const Angle* nextAngle;
1809 Segment* nextSegment;
1810 do {
1811 if (nextIndex == angleCount) {
1812 nextIndex = 0;
1813 }
1814 nextAngle = sorted[nextIndex];
1815 nextSegment = nextAngle->segment();
1816 if (!nextSegment->done(*nextAngle)) {
1817 break;
1818 }
1819 if (++nextIndex == lastIndex) {
1820 return NULL;
1821 }
1822 } while (true);
1823 nextStart = nextAngle->start();
1824 nextEnd = nextAngle->end();
1825 return nextSegment;
1826 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001827
1828 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
1829 int angleCount = sorted.count();
1830 int firstIndex = -1;
1831 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
1832 const Angle* angle = sorted[angleIndex];
1833 if (angle->segment() == this && angle->start() == end &&
1834 angle->end() == start) {
1835 firstIndex = angleIndex;
1836 break;
1837 }
1838 }
1839 return firstIndex;
1840 }
1841
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001842 // FIXME: this is tricky code; needs its own unit test
caryclark@google.comc899ad92012-08-23 15:24:42 +00001843 void findTooCloseToCall(int xorMask) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001844 int count = fTs.count();
1845 if (count < 3) { // require t=0, x, 1 at minimum
1846 return;
1847 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001848 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001849 int moCount;
1850 Span* match;
1851 Segment* mOther;
1852 do {
1853 match = &fTs[matchIndex];
1854 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00001855 // FIXME: allow quads, cubics to be near coincident?
1856 if (mOther->fVerb == SkPath::kLine_Verb) {
1857 moCount = mOther->fTs.count();
1858 if (moCount >= 3) {
1859 break;
1860 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001861 }
1862 if (++matchIndex >= count) {
1863 return;
1864 }
1865 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00001866 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001867 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001868 // look for a pair of nearby T values that map to the same (x,y) value
1869 // if found, see if the pair of other segments share a common point. If
1870 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00001871 for (int index = matchIndex + 1; index < count; ++index) {
1872 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001873 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001874 continue;
1875 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001876 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00001877 if (tOther->fVerb != SkPath::kLine_Verb) {
1878 continue; // FIXME: allow quads, cubics to be near coincident?
1879 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001880 int toCount = tOther->fTs.count();
1881 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001882 continue;
1883 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001884 const SkPoint* testPt = &xyAtT(test);
1885 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001886 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001887 moCount = toCount;
1888 match = test;
1889 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001890 matchPt = testPt;
1891 continue;
1892 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001893 int moStart = -1;
1894 int moEnd = -1;
1895 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001896 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001897 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001898 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001899 continue;
1900 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001901 if (moSpan.fOther == this) {
1902 if (moSpan.fOtherT == match->fT) {
1903 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001904 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001905 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001906 continue;
1907 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001908 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001909 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
1910 moStart = -1;
1911 break;
1912 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001913 SkASSERT(moEnd == -1);
1914 moEnd = moIndex;
1915 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001916 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001917 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001918 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001919 continue;
1920 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001921 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001922 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001923 continue;
1924 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001925 int toStart = -1;
1926 int toEnd = -1;
1927 double toStartT, toEndT;
1928 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
1929 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00001930 if (toSpan.fDone) {
1931 continue;
1932 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001933 if (toSpan.fOther == this) {
1934 if (toSpan.fOtherT == test->fT) {
1935 toStart = toIndex;
1936 toStartT = toSpan.fT;
1937 }
1938 continue;
1939 }
1940 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001941 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
1942 moStart = -1;
1943 break;
1944 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001945 SkASSERT(toEnd == -1);
1946 toEnd = toIndex;
1947 toEndT = toSpan.fT;
1948 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001949 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001950 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
1951 if (toStart <= 0 || toEnd <= 0) {
1952 continue;
1953 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001954 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001955 continue;
1956 }
1957 // test to see if the segment between there and here is linear
1958 if (!mOther->isLinear(moStart, moEnd)
1959 || !tOther->isLinear(toStart, toEnd)) {
1960 continue;
1961 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00001962 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001963 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001964 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001965 } else {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001966 mOther->addTCoincident(xorMask, moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001967 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001968 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001969 }
1970
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001971 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
1972 // and use more concise logic like the old edge walker code?
1973 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001974 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001975 // iterate through T intersections and return topmost
1976 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001977 SkASSERT(!done());
1978 int firstT;
1979 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001980 SkPoint topPt;
1981 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001982 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001983 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001984 bool lastDone = true;
1985 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001986 const Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001987 if (!span.fDone || !lastDone) {
1988 const SkPoint& intercept = xyAtT(&span);
1989 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
1990 && topPt.fX > intercept.fX)) {
1991 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001992 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001993 } else if (topPt == intercept) {
1994 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001995 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001996 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001997 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001998 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001999 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002000 int step = 1;
2001 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002002 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002003 step = -1;
2004 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002005 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002006 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002007 // if the topmost T is not on end, or is three-way or more, find left
2008 // look for left-ness from tLeft to firstT (matching y of other)
2009 SkTDArray<Angle> angles;
2010 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002011 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002012 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002013 SkTDArray<Angle*> sorted;
2014 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002015 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00002016 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002017 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002018 // skip edges that have already been processed
2019 firstT = -1;
2020 Segment* leftSegment;
2021 do {
2022 const Angle* angle = sorted[++firstT];
2023 leftSegment = angle->segment();
2024 tIndex = angle->end();
2025 endIndex = angle->start();
2026 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002027 return leftSegment;
2028 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002029
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002030 // FIXME: not crazy about this
2031 // when the intersections are performed, the other index is into an
2032 // incomplete array. as the array grows, the indices become incorrect
2033 // while the following fixes the indices up again, it isn't smart about
2034 // skipping segments whose indices are already correct
2035 // assuming we leave the code that wrote the index in the first place
2036 void fixOtherTIndex() {
2037 int iCount = fTs.count();
2038 for (int i = 0; i < iCount; ++i) {
2039 Span& iSpan = fTs[i];
2040 double oT = iSpan.fOtherT;
2041 Segment* other = iSpan.fOther;
2042 int oCount = other->fTs.count();
2043 for (int o = 0; o < oCount; ++o) {
2044 Span& oSpan = other->fTs[o];
2045 if (oT == oSpan.fT && this == oSpan.fOther) {
2046 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002047 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002048 }
2049 }
2050 }
2051 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002052
caryclark@google.com495f8e42012-05-31 13:13:11 +00002053 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002054 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002055 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002056 SkASSERT(end >= 0);
2057 if (multipleSpans(end)) {
2058 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002059 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002060 const Span& endSpan = fTs[end];
2061 Segment* other = endSpan.fOther;
2062 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002063 int otherEnd = other->nextSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002064 Span* last = other->innerChaseDone(index, step, winding);
2065 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002066 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002067 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002068
caryclark@google.com59823f72012-08-09 18:17:47 +00002069 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002070 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002071 SkASSERT(end >= 0);
2072 if (multipleSpans(end)) {
2073 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002074 }
2075 const Span& endSpan = fTs[end];
2076 Segment* other = endSpan.fOther;
2077 index = endSpan.fOtherIndex;
2078 int otherEnd = other->nextSpan(index, step);
2079 int min = SkMin32(index, otherEnd);
2080 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002081 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002082 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002083 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002084 Span* last = other->innerChaseWinding(index, step, winding);
2085 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002086 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002087 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002088
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002089 void init(const SkPoint pts[], SkPath::Verb verb) {
2090 fPts = pts;
2091 fVerb = verb;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002092 fDoneSpans = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002093 }
2094
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002095 bool intersected() const {
2096 return fTs.count() > 0;
2097 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002098
2099 bool isConnected(int startIndex, int endIndex) const {
2100 return fTs[startIndex].fWindSum != SK_MinS32
2101 || fTs[endIndex].fWindSum != SK_MinS32;
2102 }
2103
caryclark@google.com15fa1382012-05-07 20:49:36 +00002104 bool isLinear(int start, int end) const {
2105 if (fVerb == SkPath::kLine_Verb) {
2106 return true;
2107 }
2108 if (fVerb == SkPath::kQuad_Verb) {
2109 SkPoint qPart[3];
2110 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2111 return QuadIsLinear(qPart);
2112 } else {
2113 SkASSERT(fVerb == SkPath::kCubic_Verb);
2114 SkPoint cPart[4];
2115 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2116 return CubicIsLinear(cPart);
2117 }
2118 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002119
2120 // OPTIMIZE: successive calls could start were the last leaves off
2121 // or calls could specialize to walk forwards or backwards
2122 bool isMissing(double startT) const {
2123 size_t tCount = fTs.count();
2124 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002125 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002126 return false;
2127 }
2128 }
2129 return true;
2130 }
2131
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002132 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002133 int count = fTs.count();
2134 if (count == 2) {
2135 return true;
2136 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002137 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002138 if (approximately_less_than_zero(t)) {
2139 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002140 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002141 if (approximately_greater_than_one(t)) {
2142 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002143 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002144 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002145 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002146
2147 bool isHorizontal() const {
2148 return fBounds.fTop == fBounds.fBottom;
2149 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002150
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002151 bool isVertical() const {
2152 return fBounds.fLeft == fBounds.fRight;
2153 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002154
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002155 SkScalar leftMost(int start, int end) const {
2156 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2157 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002158
caryclark@google.com495f8e42012-05-31 13:13:11 +00002159 // this span is excluded by the winding rule -- chase the ends
2160 // as long as they are unambiguous to mark connections as done
2161 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002162 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002163 int index = angle->start();
2164 int endIndex = angle->end();
2165 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002166 Span* last = innerChaseDone(index, step, winding);
2167 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002168 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002169 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002170
caryclark@google.com59823f72012-08-09 18:17:47 +00002171 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002172 int index = angle->start();
2173 int endIndex = angle->end();
2174 int min = SkMin32(index, endIndex);
2175 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002176 Span* last = innerChaseWinding(index, step, winding);
2177 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002178 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002179 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002180
caryclark@google.com495f8e42012-05-31 13:13:11 +00002181 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002182 // This may be called when the segment is already marked done. While this
2183 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002184 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002185 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002186 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002187 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002188 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002189 double referenceT = fTs[index].fT;
2190 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002191 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002192 markOneDone(__FUNCTION__, lesser, winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002193 }
2194 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002195 markOneDone(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002196 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002197 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002198
caryclark@google.com24bec792012-08-20 12:43:57 +00002199 void markOneDone(const char* funName, int tIndex, int winding) {
2200 Span* span = markOneWinding(funName, tIndex, winding);
2201 if (!span) {
2202 return;
2203 }
2204 span->fDone = true;
2205 fDoneSpans++;
2206 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002207
caryclark@google.com24bec792012-08-20 12:43:57 +00002208 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2209 Span& span = fTs[tIndex];
2210 if (span.fDone) {
2211 return NULL;
2212 }
2213 #if DEBUG_MARK_DONE
2214 debugShowNewWinding(funName, span, winding);
2215 #endif
2216 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002217 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002218 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002219 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002220 span.fWindSum = winding;
2221 return &span;
2222 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002223
caryclark@google.com59823f72012-08-09 18:17:47 +00002224 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002225 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002226 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002227 double referenceT = fTs[index].fT;
2228 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002229 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002230 markOneWinding(__FUNCTION__, lesser, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002231 }
2232 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002233 markOneWinding(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002234 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002235 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002236
caryclark@google.com2ddff932012-08-07 21:25:27 +00002237 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002238 int nextDoorWind = SK_MaxS32;
2239 if (tIndex > 0) {
2240 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002241 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002242 nextDoorWind = below.fWindValue;
2243 }
2244 }
2245 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2246 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002247 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002248 nextDoorWind = above.fWindValue;
2249 }
2250 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002251 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2252 const Span& below = fTs[tIndex - 1];
2253 nextDoorWind = below.fWindValue;
2254 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002255 if (nextDoorWind != SK_MaxS32) {
2256 Span& newSpan = fTs[tIndex];
2257 newSpan.fWindValue = nextDoorWind;
2258 if (!nextDoorWind) {
2259 newSpan.fDone = true;
2260 ++fDoneSpans;
2261 }
2262 }
2263 }
2264
caryclark@google.com9764cc62012-07-12 19:29:45 +00002265 // return span if when chasing, two or more radiating spans are not done
2266 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2267 // candidate and the remaining spans have windValue == 0 (canceled by
2268 // coincidence). The coincident edges could either be removed altogether,
2269 // or this code could be more complicated in detecting this case. Worth it?
2270 bool multipleSpans(int end) const {
2271 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002272 }
2273
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002274 // This has callers for two different situations: one establishes the end
2275 // of the current span, and one establishes the beginning of the next span
2276 // (thus the name). When this is looking for the end of the current span,
2277 // coincidence is found when the beginning Ts contain -step and the end
2278 // contains step. When it is looking for the beginning of the next, the
2279 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002280 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002281 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002282 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002283 int count = fTs.count();
2284 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002285 while (step > 0 ? ++to < count : --to >= 0) {
2286 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002287 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002288 continue;
2289 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002290 return to;
2291 }
2292 return -1;
2293 }
2294
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002295 const SkPoint* pts() const {
2296 return fPts;
2297 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002298
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002299 void reset() {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002300 init(NULL, (SkPath::Verb) -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002301 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2302 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002303 }
2304
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002305 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002306 const Span& span(int tIndex) const {
2307 return fTs[tIndex];
2308 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002309
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002310 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002311 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue :
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002312 fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002313#if DEBUG_WIND_BUMP
2314 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2315#endif
2316 return result;
2317 }
2318
2319 int spanSign(const Angle* angle) const {
2320 SkASSERT(angle->segment() == this);
2321 return spanSign(angle->start(), angle->end());
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002322 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002323
2324 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002325 double t(int tIndex) const {
2326 return fTs[tIndex].fT;
2327 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002328
caryclark@google.com18063442012-07-25 12:05:18 +00002329 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
2330 double start) {
2331 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002332 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00002333 *outsideTs.append() = end;
2334 *outsideTs.append() = start;
2335 }
2336 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002337
caryclark@google.com24bec792012-08-20 12:43:57 +00002338 void undoneSpan(int& start, int& end) {
2339 size_t tCount = fTs.count();
2340 size_t index;
2341 for (index = 0; index < tCount; ++index) {
2342 if (!fTs[index].fDone) {
2343 break;
2344 }
2345 }
2346 SkASSERT(index < tCount - 1);
2347 start = index;
2348 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002349 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00002350 SkASSERT(index < tCount);
2351 SkASSERT(index < tCount);
2352 end = index;
2353 }
caryclark@google.com18063442012-07-25 12:05:18 +00002354
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002355 void updatePts(const SkPoint pts[]) {
2356 fPts = pts;
2357 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002358
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002359 SkPath::Verb verb() const {
2360 return fVerb;
2361 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002362
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002363 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002364 return fTs[tIndex].fWindSum;
2365 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002366
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002367 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002368 int start = angle->start();
2369 int end = angle->end();
2370 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002371 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002372 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002373
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002374 int windValue(int tIndex) const {
2375 return fTs[tIndex].fWindValue;
2376 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002377
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002378 int windValue(const Angle* angle) const {
2379 int start = angle->start();
2380 int end = angle->end();
2381 int index = SkMin32(start, end);
2382 return windValue(index);
2383 }
2384
2385 SkScalar xAtT(const Span* span) const {
2386 return xyAtT(span).fX;
2387 }
2388
2389 const SkPoint& xyAtT(int index) const {
2390 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002391 }
2392
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002393 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002394 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002395 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002396 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002397 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002398 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002399 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002400 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002401 }
2402 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002403 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002404 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002405
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002406 SkScalar yAtT(int index) const {
2407 return yAtT(&fTs[index]);
2408 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002409
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002410 SkScalar yAtT(const Span* span) const {
2411 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002412 }
2413
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002414#if DEBUG_DUMP
2415 void dump() const {
2416 const char className[] = "Segment";
2417 const int tab = 4;
2418 for (int i = 0; i < fTs.count(); ++i) {
2419 SkPoint out;
2420 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
2421 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002422 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002423 tab + sizeof(className), className, fID,
2424 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002425 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002426 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002427 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002428 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00002429 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002430 }
2431#endif
2432
caryclark@google.com47580692012-07-23 12:14:49 +00002433#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00002434 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002435 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00002436 for (int i = 0; i < fTs.count(); ++i) {
2437 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
2438 return;
2439 }
2440 }
2441 SkASSERT(0);
2442 }
2443#endif
2444
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002445#if DEBUG_DUMP
2446 int debugID() const {
2447 return fID;
2448 }
2449#endif
2450
caryclark@google.com24bec792012-08-20 12:43:57 +00002451#if DEBUG_WINDING
2452 void debugShowSums() const {
2453 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
2454 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
2455 for (int i = 0; i < fTs.count(); ++i) {
2456 const Span& span = fTs[i];
2457 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
2458 if (span.fWindSum == SK_MinS32) {
2459 SkDebugf("?");
2460 } else {
2461 SkDebugf("%d", span.fWindSum);
2462 }
2463 SkDebugf("]");
2464 }
2465 SkDebugf("\n");
2466 }
2467#endif
2468
caryclark@google.comcc905052012-07-25 20:59:42 +00002469#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002470 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00002471 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00002472 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00002473 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00002474 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
2475 }
2476 SkDebugf("\n");
2477 }
2478#endif
2479
caryclark@google.com027de222012-07-12 12:52:50 +00002480#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002481 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00002482 if (done()) {
2483 return;
2484 }
2485 for (int i = 0; i < fTs.count(); ++i) {
2486 if (fTs[i].fDone) {
2487 continue;
2488 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002489 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00002490 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2491 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2492 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2493 }
2494 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00002495 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00002496 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00002497 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002498 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
2499 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
2500 if (fTs[i].fWindSum == SK_MinS32) {
2501 SkDebugf("?");
2502 } else {
2503 SkDebugf("%d", fTs[i].fWindSum);
2504 }
2505 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00002506 }
2507 }
2508#endif
2509
caryclark@google.com0c803d02012-08-06 11:15:47 +00002510#if DEBUG_MARK_DONE
2511 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
2512 const SkPoint& pt = xyAtT(&span);
2513 SkDebugf("%s id=%d", fun, fID);
2514 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2515 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2516 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2517 }
2518 SkDebugf(") t=%1.9g (%1.9g,%1.9g) newWindSum=%d windSum=",
2519 span.fT, pt.fX, pt.fY, winding);
2520 if (span.fWindSum == SK_MinS32) {
2521 SkDebugf("?");
2522 } else {
2523 SkDebugf("%d", span.fWindSum);
2524 }
2525 SkDebugf(" windValue=%d\n", span.fWindValue);
2526 }
2527#endif
2528
caryclark@google.com47580692012-07-23 12:14:49 +00002529#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002530 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002531 const int contourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002532 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00002533 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002534 int lastSum = contourWinding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002535 int windSum = lastSum - spanSign(angles[first]);
caryclark@google.com03f97062012-08-21 13:13:52 +00002536 SkDebugf("%s %s contourWinding=%d sign=%d\n", fun, __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +00002537 contourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00002538 int index = first;
2539 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00002540 do {
2541 const Angle& angle = *angles[index];
2542 const Segment& segment = *angle.segment();
2543 int start = angle.start();
2544 int end = angle.end();
2545 const Span& sSpan = segment.fTs[start];
2546 const Span& eSpan = segment.fTs[end];
2547 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002548 if (!firstTime) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002549 lastSum = windSum;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002550 windSum -= segment.spanSign(&angle);
caryclark@google.comafe56de2012-07-24 18:11:03 +00002551 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002552 SkDebugf("%s [%d] id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
caryclark@google.comc899ad92012-08-23 15:24:42 +00002553 " sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
2554 __FUNCTION__, index, segment.fID, kLVerbStr[segment.fVerb],
2555 start, segment.xAtT(&sSpan),
2556 segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
2557 segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
2558 lastSum, windSum, useInnerWinding(lastSum, windSum)
2559 ? windSum : lastSum, mSpan.fDone);
2560#if DEBUG_ANGLE
2561 angle.debugShow(segment.xyAtT(&sSpan));
2562#endif
caryclark@google.com47580692012-07-23 12:14:49 +00002563 ++index;
2564 if (index == angles.count()) {
2565 index = 0;
2566 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002567 if (firstTime) {
2568 firstTime = false;
2569 }
caryclark@google.com47580692012-07-23 12:14:49 +00002570 } while (index != first);
2571 }
2572#endif
2573
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002574#if DEBUG_WINDING
2575 bool debugVerifyWinding(int start, int end, int winding) const {
2576 const Span& span = fTs[SkMin32(start, end)];
2577 int spanWinding = span.fWindSum;
2578 if (spanWinding == SK_MinS32) {
2579 return true;
2580 }
2581 int spanSign = SkSign32(start - end);
2582 int signedVal = spanSign * span.fWindValue;
2583 if (signedVal < 0) {
2584 spanWinding -= signedVal;
2585 }
2586 return span.fWindSum == winding;
2587 }
2588#endif
2589
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002590private:
2591 const SkPoint* fPts;
2592 SkPath::Verb fVerb;
2593 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002594 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00002595 int fDoneSpans; // quick check that segment is finished
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002596#if DEBUG_DUMP
2597 int fID;
2598#endif
2599};
2600
caryclark@google.comb9738012012-07-03 19:53:30 +00002601class Contour;
2602
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002603struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00002604 Contour* fContours[2];
2605 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002606 double fTs[2][2];
2607};
2608
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002609class Contour {
2610public:
2611 Contour() {
2612 reset();
2613#if DEBUG_DUMP
2614 fID = ++gContourID;
2615#endif
2616 }
2617
2618 bool operator<(const Contour& rh) const {
2619 return fBounds.fTop == rh.fBounds.fTop
2620 ? fBounds.fLeft < rh.fBounds.fLeft
2621 : fBounds.fTop < rh.fBounds.fTop;
2622 }
2623
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002624 void addCoincident(int index, Contour* other, int otherIndex,
2625 const Intersections& ts, bool swap) {
2626 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00002627 coincidence.fContours[0] = this;
2628 coincidence.fContours[1] = other;
2629 coincidence.fSegments[0] = index;
2630 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00002631 if (fSegments[index].verb() == SkPath::kLine_Verb &&
2632 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
2633 // FIXME: coincident lines use legacy Ts instead of coincident Ts
2634 coincidence.fTs[swap][0] = ts.fT[0][0];
2635 coincidence.fTs[swap][1] = ts.fT[0][1];
2636 coincidence.fTs[!swap][0] = ts.fT[1][0];
2637 coincidence.fTs[!swap][1] = ts.fT[1][1];
2638 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
2639 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
2640 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
2641 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
2642 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
2643 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
2644 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002645 }
2646
2647 void addCross(const Contour* crosser) {
2648#ifdef DEBUG_CROSS
2649 for (int index = 0; index < fCrosses.count(); ++index) {
2650 SkASSERT(fCrosses[index] != crosser);
2651 }
2652#endif
2653 *fCrosses.append() = crosser;
2654 }
2655
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002656 void addCubic(const SkPoint pts[4]) {
2657 fSegments.push_back().addCubic(pts);
2658 fContainsCurves = true;
2659 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002660
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002661 int addLine(const SkPoint pts[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002662 fSegments.push_back().addLine(pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002663 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002664 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002665
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002666 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
2667 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
2668 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002669
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002670 int addQuad(const SkPoint pts[3]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002671 fSegments.push_back().addQuad(pts);
2672 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002673 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002674 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002675
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002676 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
2677 containsIntercepts();
2678 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
2679 }
2680
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002681 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002682 return fBounds;
2683 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002684
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002685 void complete() {
2686 setBounds();
2687 fContainsIntercepts = false;
2688 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002689
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002690 void containsIntercepts() {
2691 fContainsIntercepts = true;
2692 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002693
rmistry@google.comd6176b02012-08-23 18:14:13 +00002694 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002695 int &tIndex, double& hitT) {
2696 int segmentCount = fSegments.count();
2697 const Segment* bestSegment = NULL;
2698 for (int test = 0; test < segmentCount; ++test) {
2699 Segment* testSegment = &fSegments[test];
2700 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002701 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002702 continue;
2703 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002704 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002705 continue;
2706 }
2707 if (bounds.fLeft > basePt.fX) {
2708 continue;
2709 }
2710 if (bounds.fRight < basePt.fX) {
2711 continue;
2712 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002713 if (bounds.fLeft == bounds.fRight) {
2714 continue;
2715 }
2716 #if 0
2717 bool leftHalf = bounds.fLeft == basePt.fX;
2718 bool rightHalf = bounds.fRight == basePt.fX;
2719 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
2720 basePt, leftHalf, rightHalf)) {
2721 continue;
2722 }
2723 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002724 double testHitT;
2725 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
2726 if (testT >= 0) {
2727 bestSegment = testSegment;
2728 tIndex = testT;
2729 hitT = testHitT;
2730 }
2731 }
2732 return bestSegment;
2733 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002734
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002735 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002736 for (int index = 0; index < fCrosses.count(); ++index) {
2737 if (fCrosses[index] == crosser) {
2738 return true;
2739 }
2740 }
2741 return false;
2742 }
2743
caryclark@google.comc899ad92012-08-23 15:24:42 +00002744 void findTooCloseToCall(int xorMask) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002745 int segmentCount = fSegments.count();
2746 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002747 fSegments[sIndex].findTooCloseToCall(xorMask);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002748 }
2749 }
2750
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002751 void fixOtherTIndex() {
2752 int segmentCount = fSegments.count();
2753 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
2754 fSegments[sIndex].fixOtherTIndex();
2755 }
2756 }
2757
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002758 void reset() {
2759 fSegments.reset();
2760 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002761 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002762 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002763
caryclark@google.com24bec792012-08-20 12:43:57 +00002764 void resolveCoincidence(int xorMask) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002765 int count = fCoincidences.count();
2766 for (int index = 0; index < count; ++index) {
2767 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00002768 Contour* thisContour = coincidence.fContours[0];
2769 Contour* otherContour = coincidence.fContours[1];
2770 int thisIndex = coincidence.fSegments[0];
2771 int otherIndex = coincidence.fSegments[1];
2772 Segment& thisOne = thisContour->fSegments[thisIndex];
2773 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00002774 #if DEBUG_CONCIDENT
2775 thisOne.debugShowTs();
2776 other.debugShowTs();
2777 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002778 double startT = coincidence.fTs[0][0];
2779 double endT = coincidence.fTs[0][1];
caryclark@google.com32546db2012-08-31 20:55:07 +00002780 bool opposite = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002781 if (startT > endT) {
2782 SkTSwap<double>(startT, endT);
caryclark@google.com32546db2012-08-31 20:55:07 +00002783 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002784 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002785 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002786 double oStartT = coincidence.fTs[1][0];
2787 double oEndT = coincidence.fTs[1][1];
2788 if (oStartT > oEndT) {
2789 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com32546db2012-08-31 20:55:07 +00002790 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002791 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002792 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com32546db2012-08-31 20:55:07 +00002793 if (opposite) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002794 // make sure startT and endT have t entries
caryclark@google.com32546db2012-08-31 20:55:07 +00002795 SkASSERT(opposite);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002796 if (startT > 0 || oEndT < 1
2797 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
2798 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00002799 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002800 if (oStartT > 0 || endT < 1
2801 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
2802 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00002803 }
2804 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002805 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00002806 if (startT > 0 || oStartT > 0
2807 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002808 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00002809 }
caryclark@google.com200c2112012-08-03 15:05:04 +00002810 if (endT < 1 || oEndT < 1
2811 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002812 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00002813 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002814 thisOne.addTCoincident(xorMask, startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002815 }
caryclark@google.com47580692012-07-23 12:14:49 +00002816 #if DEBUG_CONCIDENT
2817 thisOne.debugShowTs();
2818 other.debugShowTs();
2819 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002820 }
2821 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002822
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002823 const SkTArray<Segment>& segments() {
2824 return fSegments;
2825 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002826
caryclark@google.com15fa1382012-05-07 20:49:36 +00002827 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
2828 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00002829 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00002830 // segments' top, and not a true sort, so it could be ameniable to regular
2831 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002832 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002833 int segmentCount = fSegments.count();
2834 SkASSERT(segmentCount > 0);
2835 int best = -1;
2836 Segment* bestSegment = NULL;
2837 while (++best < segmentCount) {
2838 Segment* testSegment = &fSegments[best];
2839 if (testSegment->done()) {
2840 continue;
2841 }
2842 bestSegment = testSegment;
2843 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002844 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002845 if (!bestSegment) {
2846 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002847 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002848 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002849 for (int test = best + 1; test < segmentCount; ++test) {
2850 Segment* testSegment = &fSegments[test];
2851 if (testSegment->done()) {
2852 continue;
2853 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002854 if (testSegment->bounds().fTop > bestTop) {
2855 continue;
2856 }
2857 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002858 if (bestTop > testTop) {
2859 bestTop = testTop;
2860 bestSegment = testSegment;
2861 }
2862 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002863 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002864 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002865 }
2866
caryclark@google.com24bec792012-08-20 12:43:57 +00002867 Segment* undoneSegment(int& start, int& end) {
2868 int segmentCount = fSegments.count();
2869 for (int test = 0; test < segmentCount; ++test) {
2870 Segment* testSegment = &fSegments[test];
2871 if (testSegment->done()) {
2872 continue;
2873 }
2874 testSegment->undoneSpan(start, end);
2875 return testSegment;
2876 }
2877 return NULL;
2878 }
2879
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002880 int updateSegment(int index, const SkPoint* pts) {
2881 Segment& segment = fSegments[index];
2882 segment.updatePts(pts);
2883 return segment.verb() + 1;
2884 }
2885
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002886#if DEBUG_TEST
2887 SkTArray<Segment>& debugSegments() {
2888 return fSegments;
2889 }
2890#endif
2891
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002892#if DEBUG_DUMP
2893 void dump() {
2894 int i;
2895 const char className[] = "Contour";
2896 const int tab = 4;
2897 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
2898 for (i = 0; i < fSegments.count(); ++i) {
2899 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
2900 className, i);
2901 fSegments[i].dump();
2902 }
2903 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
2904 tab + sizeof(className), className,
2905 fBounds.fLeft, fBounds.fTop,
2906 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002907 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
2908 className, fContainsIntercepts);
2909 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
2910 className, fContainsCurves);
2911 }
2912#endif
2913
caryclark@google.com027de222012-07-12 12:52:50 +00002914#if DEBUG_ACTIVE_SPANS
2915 void debugShowActiveSpans() {
2916 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002917 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00002918 }
2919 }
2920#endif
2921
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002922protected:
2923 void setBounds() {
2924 int count = fSegments.count();
2925 if (count == 0) {
2926 SkDebugf("%s empty contour\n", __FUNCTION__);
2927 SkASSERT(0);
2928 // FIXME: delete empty contour?
2929 return;
2930 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002931 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002932 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002933 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002934 }
2935 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002936
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002937private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002938 SkTArray<Segment> fSegments;
2939 SkTDArray<Coincidence> fCoincidences;
2940 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002941 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002942 bool fContainsIntercepts;
2943 bool fContainsCurves;
2944#if DEBUG_DUMP
2945 int fID;
2946#endif
2947};
2948
2949class EdgeBuilder {
2950public:
2951
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002952EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002953 : fPath(path)
2954 , fCurrentContour(NULL)
2955 , fContours(contours)
2956{
2957#if DEBUG_DUMP
2958 gContourID = 0;
2959 gSegmentID = 0;
2960#endif
2961 walk();
2962}
2963
2964protected:
2965
2966void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002967 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002968 fCurrentContour->complete();
2969 fCurrentContour = NULL;
2970 }
2971}
2972
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002973void walk() {
2974 // FIXME:remove once we can access path pts directly
2975 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
2976 SkPoint pts[4];
2977 SkPath::Verb verb;
2978 do {
2979 verb = iter.next(pts);
2980 *fPathVerbs.append() = verb;
2981 if (verb == SkPath::kMove_Verb) {
2982 *fPathPts.append() = pts[0];
2983 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
2984 fPathPts.append(verb, &pts[1]);
2985 }
2986 } while (verb != SkPath::kDone_Verb);
2987 // FIXME: end of section to remove once path pts are accessed directly
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002988
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002989 SkPath::Verb reducedVerb;
2990 uint8_t* verbPtr = fPathVerbs.begin();
2991 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002992 const SkPoint* finalCurveStart = NULL;
2993 const SkPoint* finalCurveEnd = NULL;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002994 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
2995 switch (verb) {
2996 case SkPath::kMove_Verb:
2997 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002998 if (!fCurrentContour) {
2999 fCurrentContour = fContours.push_back_n(1);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003000 *fExtra.append() = -1; // start new contour
3001 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003002 finalCurveEnd = pointsPtr++;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003003 continue;
3004 case SkPath::kLine_Verb:
3005 // skip degenerate points
3006 if (pointsPtr[-1].fX != pointsPtr[0].fX
3007 || pointsPtr[-1].fY != pointsPtr[0].fY) {
3008 fCurrentContour->addLine(&pointsPtr[-1]);
3009 }
3010 break;
3011 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00003012
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003013 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
3014 if (reducedVerb == 0) {
3015 break; // skip degenerate points
3016 }
3017 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00003018 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003019 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003020 break;
3021 }
3022 fCurrentContour->addQuad(&pointsPtr[-1]);
3023 break;
3024 case SkPath::kCubic_Verb:
3025 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
3026 if (reducedVerb == 0) {
3027 break; // skip degenerate points
3028 }
3029 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003030 *fExtra.append() =
3031 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003032 break;
3033 }
3034 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003035 *fExtra.append() =
3036 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003037 break;
3038 }
3039 fCurrentContour->addCubic(&pointsPtr[-1]);
3040 break;
3041 case SkPath::kClose_Verb:
3042 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003043 if (finalCurveStart && finalCurveEnd
3044 && *finalCurveStart != *finalCurveEnd) {
3045 *fReducePts.append() = *finalCurveStart;
3046 *fReducePts.append() = *finalCurveEnd;
3047 *fExtra.append() =
3048 fCurrentContour->addLine(fReducePts.end() - 2);
3049 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003050 complete();
3051 continue;
3052 default:
3053 SkDEBUGFAIL("bad verb");
3054 return;
3055 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003056 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003057 pointsPtr += verb;
3058 SkASSERT(fCurrentContour);
3059 }
3060 complete();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003061 if (fCurrentContour && !fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003062 fContours.pop_back();
3063 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003064 // correct pointers in contours since fReducePts may have moved as it grew
3065 int cIndex = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003066 int extraCount = fExtra.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003067 SkASSERT(extraCount == 0 || fExtra[0] == -1);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003068 int eIndex = 0;
3069 int rIndex = 0;
3070 while (++eIndex < extraCount) {
3071 int offset = fExtra[eIndex];
3072 if (offset < 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003073 ++cIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003074 continue;
3075 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003076 fCurrentContour = &fContours[cIndex];
3077 rIndex += fCurrentContour->updateSegment(offset - 1,
3078 &fReducePts[rIndex]);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003079 }
3080 fExtra.reset(); // we're done with this
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003081}
3082
3083private:
3084 const SkPath& fPath;
3085 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003086 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003087 Contour* fCurrentContour;
3088 SkTArray<Contour>& fContours;
3089 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003090 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003091};
3092
3093class Work {
3094public:
3095 enum SegmentType {
3096 kHorizontalLine_Segment = -1,
3097 kVerticalLine_Segment = 0,
3098 kLine_Segment = SkPath::kLine_Verb,
3099 kQuad_Segment = SkPath::kQuad_Verb,
3100 kCubic_Segment = SkPath::kCubic_Verb,
3101 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00003102
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003103 void addCoincident(Work& other, const Intersections& ts, bool swap) {
3104 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
3105 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003106
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003107 // FIXME: does it make sense to write otherIndex now if we're going to
3108 // fix it up later?
3109 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003110 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003111 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003112
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003113 // Avoid collapsing t values that are close to the same since
3114 // we walk ts to describe consecutive intersections. Since a pair of ts can
3115 // be nearly equal, any problems caused by this should be taken care
3116 // of later.
3117 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003118 int addT(double newT, const Work& other) {
3119 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003120 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003121
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003122 bool advance() {
3123 return ++fIndex < fLast;
3124 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003125
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003126 SkScalar bottom() const {
3127 return bounds().fBottom;
3128 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003129
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003130 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003131 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003132 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003133
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003134 const SkPoint* cubic() const {
3135 return fCubic;
3136 }
3137
3138 void init(Contour* contour) {
3139 fContour = contour;
3140 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003141 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003142 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003143
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003144 bool isAdjacent(const Work& next) {
3145 return fContour == next.fContour && fIndex + 1 == next.fIndex;
3146 }
3147
3148 bool isFirstLast(const Work& next) {
3149 return fContour == next.fContour && fIndex == 0
3150 && next.fIndex == fLast - 1;
3151 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003152
3153 SkScalar left() const {
3154 return bounds().fLeft;
3155 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003156
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003157 void promoteToCubic() {
3158 fCubic[0] = pts()[0];
3159 fCubic[2] = pts()[1];
3160 fCubic[3] = pts()[2];
3161 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
3162 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
3163 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
3164 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
3165 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003166
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003167 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003168 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003169 }
3170
3171 SkScalar right() const {
3172 return bounds().fRight;
3173 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003174
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003175 ptrdiff_t segmentIndex() const {
3176 return fIndex;
3177 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003178
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003179 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003180 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003181 SegmentType type = (SegmentType) segment.verb();
3182 if (type != kLine_Segment) {
3183 return type;
3184 }
3185 if (segment.isHorizontal()) {
3186 return kHorizontalLine_Segment;
3187 }
3188 if (segment.isVertical()) {
3189 return kVerticalLine_Segment;
3190 }
3191 return kLine_Segment;
3192 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003193
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003194 bool startAfter(const Work& after) {
3195 fIndex = after.fIndex;
3196 return advance();
3197 }
3198
3199 SkScalar top() const {
3200 return bounds().fTop;
3201 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003202
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003203 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003204 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003205 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003206
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003207 SkScalar x() const {
3208 return bounds().fLeft;
3209 }
3210
3211 bool xFlipped() const {
3212 return x() != pts()[0].fX;
3213 }
3214
3215 SkScalar y() const {
3216 return bounds().fTop;
3217 }
3218
3219 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003220 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003221 }
3222
3223protected:
3224 Contour* fContour;
3225 SkPoint fCubic[4];
3226 int fIndex;
3227 int fLast;
3228};
3229
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003230#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003231static void debugShowLineIntersection(int pts, const Work& wt,
3232 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003233 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003234 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
3235 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
3236 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
3237 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003238 return;
3239 }
3240 SkPoint wtOutPt, wnOutPt;
3241 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3242 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003243 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003244 __FUNCTION__,
3245 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3246 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
3247 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003248 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003249 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003250 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003251 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3252 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
3253 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003254 SkDebugf(" wnTs[1]=%g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003255 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003256 SkDebugf("\n");
3257}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003258#else
3259static void debugShowLineIntersection(int , const Work& ,
3260 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003261}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003262#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003263
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003264static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003265
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003266 if (test != next) {
3267 if (test->bounds().fBottom < next->bounds().fTop) {
3268 return false;
3269 }
3270 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
3271 return true;
3272 }
3273 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003274 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003275 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003276 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003277 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003278 Work wn;
3279 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003280 if (test == next && !wn.startAfter(wt)) {
3281 continue;
3282 }
3283 do {
3284 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
3285 continue;
3286 }
3287 int pts;
3288 Intersections ts;
3289 bool swap = false;
3290 switch (wt.segmentType()) {
3291 case Work::kHorizontalLine_Segment:
3292 swap = true;
3293 switch (wn.segmentType()) {
3294 case Work::kHorizontalLine_Segment:
3295 case Work::kVerticalLine_Segment:
3296 case Work::kLine_Segment: {
3297 pts = HLineIntersect(wn.pts(), wt.left(),
3298 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003299 debugShowLineIntersection(pts, wt, wn,
3300 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003301 break;
3302 }
3303 case Work::kQuad_Segment: {
3304 pts = HQuadIntersect(wn.pts(), wt.left(),
3305 wt.right(), wt.y(), wt.xFlipped(), ts);
3306 break;
3307 }
3308 case Work::kCubic_Segment: {
3309 pts = HCubicIntersect(wn.pts(), wt.left(),
3310 wt.right(), wt.y(), wt.xFlipped(), ts);
3311 break;
3312 }
3313 default:
3314 SkASSERT(0);
3315 }
3316 break;
3317 case Work::kVerticalLine_Segment:
3318 swap = true;
3319 switch (wn.segmentType()) {
3320 case Work::kHorizontalLine_Segment:
3321 case Work::kVerticalLine_Segment:
3322 case Work::kLine_Segment: {
3323 pts = VLineIntersect(wn.pts(), wt.top(),
3324 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003325 debugShowLineIntersection(pts, wt, wn,
3326 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003327 break;
3328 }
3329 case Work::kQuad_Segment: {
3330 pts = VQuadIntersect(wn.pts(), wt.top(),
3331 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3332 break;
3333 }
3334 case Work::kCubic_Segment: {
3335 pts = VCubicIntersect(wn.pts(), wt.top(),
3336 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3337 break;
3338 }
3339 default:
3340 SkASSERT(0);
3341 }
3342 break;
3343 case Work::kLine_Segment:
3344 switch (wn.segmentType()) {
3345 case Work::kHorizontalLine_Segment:
3346 pts = HLineIntersect(wt.pts(), wn.left(),
3347 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003348 debugShowLineIntersection(pts, wt, wn,
3349 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003350 break;
3351 case Work::kVerticalLine_Segment:
3352 pts = VLineIntersect(wt.pts(), wn.top(),
3353 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003354 debugShowLineIntersection(pts, wt, wn,
3355 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003356 break;
3357 case Work::kLine_Segment: {
3358 pts = LineIntersect(wt.pts(), wn.pts(), ts);
3359 debugShowLineIntersection(pts, wt, wn,
3360 ts.fT[1], ts.fT[0]);
3361 break;
3362 }
3363 case Work::kQuad_Segment: {
3364 swap = true;
3365 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
3366 break;
3367 }
3368 case Work::kCubic_Segment: {
3369 swap = true;
3370 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
3371 break;
3372 }
3373 default:
3374 SkASSERT(0);
3375 }
3376 break;
3377 case Work::kQuad_Segment:
3378 switch (wn.segmentType()) {
3379 case Work::kHorizontalLine_Segment:
3380 pts = HQuadIntersect(wt.pts(), wn.left(),
3381 wn.right(), wn.y(), wn.xFlipped(), ts);
3382 break;
3383 case Work::kVerticalLine_Segment:
3384 pts = VQuadIntersect(wt.pts(), wn.top(),
3385 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3386 break;
3387 case Work::kLine_Segment: {
3388 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
3389 break;
3390 }
3391 case Work::kQuad_Segment: {
3392 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
3393 break;
3394 }
3395 case Work::kCubic_Segment: {
3396 wt.promoteToCubic();
3397 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
3398 break;
3399 }
3400 default:
3401 SkASSERT(0);
3402 }
3403 break;
3404 case Work::kCubic_Segment:
3405 switch (wn.segmentType()) {
3406 case Work::kHorizontalLine_Segment:
3407 pts = HCubicIntersect(wt.pts(), wn.left(),
3408 wn.right(), wn.y(), wn.xFlipped(), ts);
3409 break;
3410 case Work::kVerticalLine_Segment:
3411 pts = VCubicIntersect(wt.pts(), wn.top(),
3412 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3413 break;
3414 case Work::kLine_Segment: {
3415 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
3416 break;
3417 }
3418 case Work::kQuad_Segment: {
3419 wn.promoteToCubic();
3420 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
3421 break;
3422 }
3423 case Work::kCubic_Segment: {
3424 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
3425 break;
3426 }
3427 default:
3428 SkASSERT(0);
3429 }
3430 break;
3431 default:
3432 SkASSERT(0);
3433 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003434 if (!foundCommonContour && pts > 0) {
3435 test->addCross(next);
3436 next->addCross(test);
3437 foundCommonContour = true;
3438 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003439 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00003440 if (pts == 2) {
3441 if (wn.segmentType() <= Work::kLine_Segment
3442 && wt.segmentType() <= Work::kLine_Segment) {
3443 wt.addCoincident(wn, ts, swap);
3444 continue;
3445 }
3446 if (wn.segmentType() == Work::kQuad_Segment
3447 && wt.segmentType() == Work::kQuad_Segment
3448 && ts.coincidentUsed() == 2) {
3449 wt.addCoincident(wn, ts, swap);
3450 continue;
3451 }
3452
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003453 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003454 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003455 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
3456 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003457 int testTAt = wt.addT(ts.fT[swap][pt], wn);
3458 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003459 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
3460 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003461 }
3462 } while (wn.advance());
3463 } while (wt.advance());
3464 return true;
3465}
3466
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003467// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003468// see if coincidence is formed by clipping non-concident segments
caryclark@google.com24bec792012-08-20 12:43:57 +00003469static void coincidenceCheck(SkTDArray<Contour*>& contourList, int xorMask) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003470 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00003471 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003472 Contour* contour = contourList[cIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00003473 contour->resolveCoincidence(xorMask);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003474 }
3475 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
3476 Contour* contour = contourList[cIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00003477 contour->findTooCloseToCall(xorMask);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003478 }
3479}
3480
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003481// project a ray from the top of the contour up and see if it hits anything
3482// note: when we compute line intersections, we keep track of whether
3483// two contours touch, so we need only look at contours not touching this one.
3484// OPTIMIZATION: sort contourList vertically to avoid linear walk
3485static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003486 const Segment* current, int index, int endIndex) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003487 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003488 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003489 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00003490 const Segment* test = NULL;
3491 int tIndex;
3492 double tHit;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003493 // bool checkCrosses = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003494 for (int cTest = 0; cTest < contourCount; ++cTest) {
3495 Contour* contour = contourList[cTest];
3496 if (basePt.fY < contour->bounds().fTop) {
3497 continue;
3498 }
3499 if (bestY > contour->bounds().fBottom) {
3500 continue;
3501 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003502#if 0
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003503 // even though the contours crossed, if spans cancel through concidence,
3504 // the contours may be not have any span links to chase, and the current
3505 // segment may be isolated. Detect this by seeing if current has
3506 // uninitialized wind sums. If so, project a ray instead of relying on
3507 // previously found intersections.
3508 if (baseContour == contour) {
3509 continue;
3510 }
3511 if (checkCrosses && baseContour->crosses(contour)) {
3512 if (current->isConnected(index, endIndex)) {
3513 continue;
3514 }
3515 checkCrosses = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003516 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003517#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003518 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
3519 if (next) {
3520 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003521 }
caryclark@google.com47580692012-07-23 12:14:49 +00003522 }
3523 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00003524 return 0;
3525 }
3526 int winding, windValue;
3527 // If the ray hit the end of a span, we need to construct the wheel of
3528 // angles to find the span closest to the ray -- even if there are just
3529 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003530 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003531 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00003532 SkTDArray<Angle> angles;
3533 int end = test->nextSpan(tIndex, 1);
3534 if (end < 0) {
3535 end = test->nextSpan(tIndex, -1);
3536 }
3537 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00003538 SkASSERT(angles.count() > 0);
3539 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
3540#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00003541 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00003542#endif
3543 return 0;
3544 }
caryclark@google.com47580692012-07-23 12:14:49 +00003545 test->buildAngles(tIndex, angles);
3546 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00003547 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00003548 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00003549 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00003550 // hour after 6 o'clock
3551 sortAngles(angles, sorted);
3552#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00003553 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00003554#endif
3555 // walk the sorted angle fan to find the lowest angle
3556 // above the base point. Currently, the first angle in the sorted array
3557 // is 12 noon or an earlier hour (the next counterclockwise)
3558 int count = sorted.count();
3559 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003560 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00003561 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003562 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00003563 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00003564 angle = sorted[index];
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003565 if (baseMatches && angle->isHorizontal()) {
3566 continue;
3567 }
3568 double indexDx = angle->dx();
caryclark@google.com47580692012-07-23 12:14:49 +00003569 if (indexDx < 0) {
3570 left = index;
3571 } else if (indexDx > 0) {
3572 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00003573 int previous = index - 1;
3574 if (previous < 0) {
3575 previous = count - 1;
3576 }
3577 const Angle* prev = sorted[previous];
3578 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
3579#if DEBUG_SORT
3580 SkDebugf("%s use prev\n", __FUNCTION__);
3581#endif
3582 right = previous;
3583 }
caryclark@google.com47580692012-07-23 12:14:49 +00003584 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003585 } else {
3586 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003587 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003588 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003589 if (left < 0 && right < 0) {
3590 left = mid;
3591 }
caryclark@google.com47580692012-07-23 12:14:49 +00003592 SkASSERT(left >= 0 || right >= 0);
3593 if (left < 0) {
3594 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003595 } else if (left >= 0 && mid >= 0 && right >= 0
3596 && sorted[mid]->sign() == sorted[right]->sign()) {
3597 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003598 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003599 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00003600 test = angle->segment();
3601 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00003602 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003603 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00003604#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003605 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
3606 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00003607#endif
3608 } else {
3609 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00003610 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003611 windValue = test->windValue(tIndex);
3612#if DEBUG_WINDING
3613 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
3614 windValue);
3615#endif
3616 }
3617 // see if a + change in T results in a +/- change in X (compute x'(T))
3618 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
3619#if DEBUG_WINDING
3620 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
3621#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00003622 SkASSERT(dx != 0);
3623 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00003624 winding += dx > 0 ? -windValue : windValue;
3625#if DEBUG_WINDING
3626 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
3627#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003628 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003629 return winding;
3630}
rmistry@google.comd6176b02012-08-23 18:14:13 +00003631
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003632// OPTIMIZATION: not crazy about linear search here to find top active y.
3633// seems like we should break down and do the sort, or maybe sort each
rmistry@google.comd6176b02012-08-23 18:14:13 +00003634// contours' segments?
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003635// Once the segment array is built, there's no reason I can think of not to
3636// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003637// FIXME: return the contour found to pass to inner contour check
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003638static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003639 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003640 int cIndex = 0;
3641 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003642 SkScalar bestY = SK_ScalarMax;
3643 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003644 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003645 contour = contourList[cIndex];
3646 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003647 } while (!topStart && ++cIndex < contourCount);
3648 if (!topStart) {
3649 return NULL;
3650 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003651 while (++cIndex < contourCount) {
3652 contour = contourList[cIndex];
3653 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003654 continue;
3655 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003656 SkScalar testY = SK_ScalarMax;
3657 Segment* test = contour->topSegment(testY);
3658 if (!test || bestY <= testY) {
3659 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003660 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003661 topStart = test;
3662 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003663 }
3664 return topStart;
3665}
3666
caryclark@google.com24bec792012-08-20 12:43:57 +00003667static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
3668 int contourCount = contourList.count();
3669 Segment* result;
3670 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
3671 Contour* contour = contourList[cIndex];
3672 result = contour->undoneSegment(start, end);
3673 if (result) {
3674 return result;
3675 }
3676 }
3677 return NULL;
3678}
3679
3680
3681
caryclark@google.come21cb182012-07-23 21:26:31 +00003682static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
3683 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003684 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00003685 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003686 const Span& backPtr = span->fOther->span(span->fOtherIndex);
3687 Segment* segment = backPtr.fOther;
3688 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003689 SkTDArray<Angle> angles;
3690 int done = 0;
3691 if (segment->activeAngle(tIndex, done, angles)) {
3692 Angle* last = angles.end() - 1;
3693 tIndex = last->start();
3694 endIndex = last->end();
3695 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003696 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00003697 if (done == angles.count()) {
3698 chase.pop(&span);
3699 continue;
3700 }
3701 SkTDArray<Angle*> sorted;
3702 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00003703#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00003704 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00003705#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003706 // find first angle, initialize winding to computed fWindSum
3707 int firstIndex = -1;
3708 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003709 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003710 do {
3711 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003712 segment = angle->segment();
3713 winding = segment->windSum(angle);
3714 } while (winding == SK_MinS32);
3715 int spanWinding = segment->spanSign(angle->start(), angle->end());
3716 #if DEBUG_WINDING
3717 SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
3718 __FUNCTION__, winding, spanWinding, contourWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00003719 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003720 // turn swinding into contourWinding
3721 if (spanWinding * winding < 0) {
3722 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003723 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003724 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00003725 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003726 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003727 // we care about first sign and whether wind sum indicates this
3728 // edge is inside or outside. Maybe need to pass span winding
3729 // or first winding or something into this function?
3730 // advance to first undone angle, then return it and winding
3731 // (to set whether edges are active or not)
3732 int nextIndex = firstIndex + 1;
3733 int angleCount = sorted.count();
3734 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003735 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00003736 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00003737 do {
3738 SkASSERT(nextIndex != firstIndex);
3739 if (nextIndex == angleCount) {
3740 nextIndex = 0;
3741 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003742 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00003743 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003744 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003745 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003746 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00003747 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
3748 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003749 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003750 tIndex = angle->start();
3751 endIndex = angle->end();
3752 int lesser = SkMin32(tIndex, endIndex);
3753 const Span& nextSpan = segment->span(lesser);
3754 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003755#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00003756 // FIXME: this be wrong. assign startWinding if edge is in
3757 // same direction. If the direction is opposite, winding to
3758 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00003759 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003760 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003761 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003762 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003763#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003764 break;
3765 }
3766 } while (++nextIndex != lastIndex);
3767 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003768 }
3769 return NULL;
3770}
3771
caryclark@google.com027de222012-07-12 12:52:50 +00003772#if DEBUG_ACTIVE_SPANS
3773static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
3774 for (int index = 0; index < contourList.count(); ++ index) {
3775 contourList[index]->debugShowActiveSpans();
3776 }
3777}
3778#endif
3779
caryclark@google.com27c449a2012-07-27 18:26:38 +00003780static bool windingIsActive(int winding, int spanWinding) {
3781 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
3782 && (!winding || !spanWinding || winding == -spanWinding);
3783}
3784
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003785// Each segment may have an inside or an outside. Segments contained within
3786// winding may have insides on either side, and form a contour that should be
3787// ignored. Segments that are coincident with opposing direction segments may
3788// have outsides on either side, and should also disappear.
3789// 'Normal' segments will have one inside and one outside. Subsequent connections
3790// when winding should follow the intersection direction. If more than one edge
3791// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003792 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00003793 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com24bec792012-08-20 12:43:57 +00003794static void bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003795 bool firstContour = true;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003796 do {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003797 Segment* topStart = findTopContour(contourList);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003798 if (!topStart) {
3799 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00003800 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003801 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00003802 // follow edges to intersection by changing the index by direction.
3803 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003804 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003805 int contourWinding;
3806 if (firstContour) {
3807 contourWinding = 0;
3808 firstContour = false;
3809 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003810 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003811 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00003812 if (sumWinding == SK_MinS32) {
3813 sumWinding = current->computeSum(index, endIndex);
3814 }
3815 if (sumWinding == SK_MinS32) {
3816 contourWinding = innerContourCheck(contourList, current,
3817 index, endIndex);
3818 } else {
3819 contourWinding = sumWinding;
3820 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003821 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
3822 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00003823 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003824 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003825#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00003826 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
rmistry@google.comd6176b02012-08-23 18:14:13 +00003827 sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00003828 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003829#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003830 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003831#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003832 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003833 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
3834#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003835 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003836 SkPoint lastPt;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003837 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003838 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003839 // FIXME: needs work. While it works in limited situations, it does
3840 // not always compute winding correctly. Active should be removed and instead
3841 // the initial winding should be correctly passed in so that if the
3842 // inner contour is wound the same way, it never finds an accumulated
3843 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00003844 // other than zero when resolving sorted angles.
caryclark@google.com27c449a2012-07-27 18:26:38 +00003845 bool active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003846 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003847 do {
caryclark@google.com0e08a192012-07-13 21:07:52 +00003848 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00003849 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
3850 __FUNCTION__, active ? "true" : "false",
3851 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003852 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003853 const SkPoint* firstPt = NULL;
3854 do {
3855 SkASSERT(!current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003856 int nextStart = index;
3857 int nextEnd = endIndex;
3858 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.com27c449a2012-07-27 18:26:38 +00003859 nextStart, nextEnd, winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003860 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00003861 if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
3862 lastPt = current->addCurveTo(index, endIndex, simple, true);
3863 SkASSERT(*firstPt == lastPt);
3864 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003865 break;
3866 }
3867 if (!firstPt) {
3868 firstPt = &current->addMoveTo(index, simple, active);
3869 }
3870 lastPt = current->addCurveTo(index, endIndex, simple, active);
3871 current = next;
3872 index = nextStart;
3873 endIndex = nextEnd;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003874 } while (*firstPt != lastPt && (active || !current->done()));
3875 if (firstPt && active) {
3876 #if DEBUG_PATH_CONSTRUCTION
3877 SkDebugf("%s close\n", __FUNCTION__);
3878 #endif
3879 simple.close();
3880 }
caryclark@google.come21cb182012-07-23 21:26:31 +00003881 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003882 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00003883 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003884 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003885 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003886 break;
3887 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003888 int lesser = SkMin32(index, endIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003889 spanWinding = current->spanSign(index, endIndex);
3890 winding = current->windSum(lesser);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003891 bool inner = useInnerWinding(winding - spanWinding, winding);
3892 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00003893 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
caryclark@google.com59823f72012-08-09 18:17:47 +00003894 " inner=%d result=%d\n",
caryclark@google.com2ddff932012-08-07 21:25:27 +00003895 __FUNCTION__, current->debugID(), current->t(lesser),
3896 spanWinding, winding, SkSign32(index - endIndex),
3897 useInnerWinding(winding - spanWinding, winding),
caryclark@google.com2ddff932012-08-07 21:25:27 +00003898 inner ? winding - spanWinding : winding);
3899 #endif
3900 if (inner) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003901 winding -= spanWinding;
3902 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003903 active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003904 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003905 } while (true);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003906}
3907
caryclark@google.com24bec792012-08-20 12:43:57 +00003908static void bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
3909 Segment* current;
3910 int start, end;
3911 while ((current = findUndone(contourList, start, end))) {
3912 const SkPoint* firstPt = NULL;
3913 SkPoint lastPt;
3914 do {
3915 SkASSERT(!current->done());
3916 int nextStart = start;
3917 int nextEnd = end;
3918 Segment* next = current->findNextXor(nextStart, nextEnd);
3919 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00003920 if (firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
3921 lastPt = current->addCurveTo(start, end, simple, true);
3922 SkASSERT(*firstPt == lastPt);
3923 }
caryclark@google.com24bec792012-08-20 12:43:57 +00003924 break;
3925 }
3926 if (!firstPt) {
3927 firstPt = &current->addMoveTo(start, simple, true);
3928 }
3929 lastPt = current->addCurveTo(start, end, simple, true);
3930 current = next;
3931 start = nextStart;
3932 end = nextEnd;
3933 } while (*firstPt != lastPt);
3934 if (firstPt) {
3935 #if DEBUG_PATH_CONSTRUCTION
3936 SkDebugf("%s close\n", __FUNCTION__);
3937 #endif
3938 simple.close();
3939 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003940 }
caryclark@google.com24bec792012-08-20 12:43:57 +00003941}
3942
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003943static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
3944 int contourCount = contourList.count();
3945 for (int cTest = 0; cTest < contourCount; ++cTest) {
3946 Contour* contour = contourList[cTest];
3947 contour->fixOtherTIndex();
3948 }
3949}
3950
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003951static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003952 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003953 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003954 if (count == 0) {
3955 return;
3956 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003957 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003958 *list.append() = &contours[index];
3959 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003960 QSort<Contour>(list.begin(), list.end() - 1);
3961}
3962
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003963void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003964 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.com24bec792012-08-20 12:43:57 +00003965 int xorMask = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003966 simple.reset();
3967 simple.setFillType(SkPath::kEvenOdd_FillType);
3968
3969 // turn path into list of segments
3970 SkTArray<Contour> contours;
3971 // FIXME: add self-intersecting cubics' T values to segment
3972 EdgeBuilder builder(path, contours);
3973 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003974 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003975 Contour** currentPtr = contourList.begin();
3976 if (!currentPtr) {
3977 return;
3978 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003979 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003980 // find all intersections between segments
3981 do {
3982 Contour** nextPtr = currentPtr;
3983 Contour* current = *currentPtr++;
3984 Contour* next;
3985 do {
3986 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003987 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003988 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003989 // eat through coincident edges
caryclark@google.com24bec792012-08-20 12:43:57 +00003990 coincidenceCheck(contourList, xorMask);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003991 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003992 // construct closed contours
caryclark@google.com24bec792012-08-20 12:43:57 +00003993 if (xorMask < 0) {
3994 bridgeWinding(contourList, simple);
3995 } else {
3996 bridgeXor(contourList, simple);
3997 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003998}
3999