blob: fba7e5f95b9e00949dabc9a6163e92ec471eb0c5 [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.comd0deb4f2012-12-17 13:58:08 +000027bool gUseOldBridgeWinding = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000028
caryclark@google.comf839c032012-10-26 21:03:50 +000029#define PIN_ADD_T 0
caryclark@google.com0b7da432012-10-31 19:00:20 +000030#define TRY_ROTATE 1
caryclark@google.comdb0b3e02012-12-21 21:34:36 +000031#define CHECK_IN_X 0
caryclark@google.coma461ff02012-10-11 12:54:23 +000032
caryclark@google.com47580692012-07-23 12:14:49 +000033#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.come7bd5f42012-12-13 19:47:53 +000034#define FORCE_RELEASE 0 // set force release to 1 for multiple thread -- no debugging
caryclark@google.comfa0588f2012-04-26 21:01:06 +000035
caryclark@google.com31143cf2012-11-09 22:14:19 +000036#if FORCE_RELEASE || defined SK_RELEASE
caryclark@google.com47580692012-07-23 12:14:49 +000037
38const bool gRunTestsInOneThread = false;
39
40#define DEBUG_ACTIVE_SPANS 0
caryclark@google.com4eeda372012-12-06 21:47:48 +000041#define DEBUG_ACTIVE_SPANS_SHORT_FORM 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000042#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000043#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000044#define DEBUG_ANGLE 0
caryclark@google.come7bd5f42012-12-13 19:47:53 +000045#define DEBUG_ASSEMBLE 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.come7bd5f42012-12-13 19:47:53 +000048#define DEBUG_FLOW 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000049#define DEBUG_MARK_DONE 0
caryclark@google.comf839c032012-10-26 21:03:50 +000050#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com729e1c42012-11-21 21:36:34 +000051#define DEBUG_SHOW_WINDING 0
caryclark@google.com47580692012-07-23 12:14:49 +000052#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000053#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000054#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000055
56#else
57
caryclark@google.com47580692012-07-23 12:14:49 +000058const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000059
caryclark@google.comc91dfe42012-10-16 12:06:27 +000060#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com4eeda372012-12-06 21:47:48 +000061#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000062#define DEBUG_ADD_INTERSECTING_TS 1
63#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000064#define DEBUG_ANGLE 1
caryclark@google.come7bd5f42012-12-13 19:47:53 +000065#define DEBUG_ASSEMBLE 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000066#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000067#define DEBUG_CROSS 0
caryclark@google.come7bd5f42012-12-13 19:47:53 +000068#define DEBUG_FLOW 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000069#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000070#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com729e1c42012-11-21 21:36:34 +000071#define DEBUG_SHOW_WINDING 0
caryclark@google.com47580692012-07-23 12:14:49 +000072#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000073#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000074#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000075
76#endif
77
caryclark@google.com6aea33f2012-10-09 14:11:58 +000078#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000079
caryclark@google.comfa0588f2012-04-26 21:01:06 +000080#if DEBUG_DUMP
81static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000082// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000083static int gContourID;
84static int gSegmentID;
85#endif
86
caryclark@google.com8dcf1142012-07-02 20:27:02 +000087#ifndef DEBUG_TEST
88#define DEBUG_TEST 0
89#endif
90
caryclark@google.com32546db2012-08-31 20:55:07 +000091#define MAKE_CONST_LINE(line, pts) \
92 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
93#define MAKE_CONST_QUAD(quad, pts) \
94 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
95 {pts[2].fX, pts[2].fY}}
96#define MAKE_CONST_CUBIC(cubic, pts) \
97 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
98 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
99
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000100static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
101 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000102 MAKE_CONST_LINE(aLine, a);
103 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000104 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
105}
106
107static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
108 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000109 MAKE_CONST_QUAD(aQuad, a);
110 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000111 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000112}
113
caryclark@google.com32546db2012-08-31 20:55:07 +0000114static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000115 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000116 MAKE_CONST_CUBIC(aCubic, a);
117 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000118 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
119}
120
121static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
122 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000123 MAKE_CONST_QUAD(aQuad, a);
124 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000125#define TRY_QUARTIC_SOLUTION 1
126#if TRY_QUARTIC_SOLUTION
127 intersect2(aQuad, bQuad, intersections);
128#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000129 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000130#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000131 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000132}
133
134static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
135 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000136 MAKE_CONST_CUBIC(aCubic, a);
137 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000138 intersect(aCubic, bCubic, intersections);
139 return intersections.fUsed;
140}
141
142static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
143 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000144 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000145 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
146}
147
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000148static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
149 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000150 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000151 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
152}
153
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000154static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
155 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000156 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000157 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
158}
159
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000160static int (* const HSegmentIntersect[])(const SkPoint [], SkScalar ,
161 SkScalar , SkScalar , bool , Intersections& ) = {
162 NULL,
163 HLineIntersect,
164 HQuadIntersect,
165 HCubicIntersect
166};
167
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000168static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
169 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000170 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000171 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
172}
173
174static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
175 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000176 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000177 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
178}
179
180static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
181 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000182 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000183 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000184}
185
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000186static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
187 SkScalar , SkScalar , bool , Intersections& ) = {
188 NULL,
189 VLineIntersect,
190 VQuadIntersect,
191 VCubicIntersect
192};
193
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000194static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000195 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000196 double x, y;
197 xy_at_t(line, t, x, y);
198 out->fX = SkDoubleToScalar(x);
199 out->fY = SkDoubleToScalar(y);
200}
201
202static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000203 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000204 double x, y;
205 xy_at_t(quad, t, x, y);
206 out->fX = SkDoubleToScalar(x);
207 out->fY = SkDoubleToScalar(y);
208}
209
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000210static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
211 MAKE_CONST_QUAD(quad, a);
212 xy_at_t(quad, t, out->x, out->y);
213}
214
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000215static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000216 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000217 double x, y;
218 xy_at_t(cubic, t, x, y);
219 out->fX = SkDoubleToScalar(x);
220 out->fY = SkDoubleToScalar(y);
221}
222
223static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
224 NULL,
225 LineXYAtT,
226 QuadXYAtT,
227 CubicXYAtT
228};
229
230static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000231 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000232 double x;
233 xy_at_t(aLine, t, x, *(double*) 0);
234 return SkDoubleToScalar(x);
235}
236
237static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000238 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000239 double x;
240 xy_at_t(quad, t, x, *(double*) 0);
241 return SkDoubleToScalar(x);
242}
243
244static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000245 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000246 double x;
247 xy_at_t(cubic, t, x, *(double*) 0);
248 return SkDoubleToScalar(x);
249}
250
251static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
252 NULL,
253 LineXAtT,
254 QuadXAtT,
255 CubicXAtT
256};
257
258static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000259 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000260 double y;
261 xy_at_t(aLine, t, *(double*) 0, y);
262 return SkDoubleToScalar(y);
263}
264
265static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000266 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000267 double y;
268 xy_at_t(quad, t, *(double*) 0, y);
269 return SkDoubleToScalar(y);
270}
271
272static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000273 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000274 double y;
275 xy_at_t(cubic, t, *(double*) 0, y);
276 return SkDoubleToScalar(y);
277}
278
279static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
280 NULL,
281 LineYAtT,
282 QuadYAtT,
283 CubicYAtT
284};
285
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000286static SkScalar LineDXAtT(const SkPoint a[2], double ) {
287 return a[1].fX - a[0].fX;
288}
289
290static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000291 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000292 double x;
293 dxdy_at_t(quad, t, x, *(double*) 0);
294 return SkDoubleToScalar(x);
295}
296
297static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000298 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000299 double x;
300 dxdy_at_t(cubic, t, x, *(double*) 0);
301 return SkDoubleToScalar(x);
302}
303
304static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
305 NULL,
306 LineDXAtT,
307 QuadDXAtT,
308 CubicDXAtT
309};
310
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000311static SkScalar LineDYAtT(const SkPoint a[2], double ) {
312 return a[1].fY - a[0].fY;
313}
314
315static SkScalar QuadDYAtT(const SkPoint a[3], double t) {
316 MAKE_CONST_QUAD(quad, a);
317 double y;
318 dxdy_at_t(quad, t, *(double*) 0, y);
319 return SkDoubleToScalar(y);
320}
321
322static SkScalar CubicDYAtT(const SkPoint a[4], double t) {
323 MAKE_CONST_CUBIC(cubic, a);
324 double y;
325 dxdy_at_t(cubic, t, *(double*) 0, y);
326 return SkDoubleToScalar(y);
327}
328
329static SkScalar (* const SegmentDYAtT[])(const SkPoint [], double ) = {
330 NULL,
331 LineDYAtT,
332 QuadDYAtT,
333 CubicDYAtT
334};
335
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000336static void LineSubDivide(const SkPoint a[2], double startT, double endT,
337 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000338 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000339 _Line dst;
340 sub_divide(aLine, startT, endT, dst);
341 sub[0].fX = SkDoubleToScalar(dst[0].x);
342 sub[0].fY = SkDoubleToScalar(dst[0].y);
343 sub[1].fX = SkDoubleToScalar(dst[1].x);
344 sub[1].fY = SkDoubleToScalar(dst[1].y);
345}
346
347static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
348 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000349 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000350 Quadratic dst;
351 sub_divide(aQuad, startT, endT, dst);
352 sub[0].fX = SkDoubleToScalar(dst[0].x);
353 sub[0].fY = SkDoubleToScalar(dst[0].y);
354 sub[1].fX = SkDoubleToScalar(dst[1].x);
355 sub[1].fY = SkDoubleToScalar(dst[1].y);
356 sub[2].fX = SkDoubleToScalar(dst[2].x);
357 sub[2].fY = SkDoubleToScalar(dst[2].y);
358}
359
360static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
361 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000362 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000363 Cubic dst;
364 sub_divide(aCubic, startT, endT, dst);
365 sub[0].fX = SkDoubleToScalar(dst[0].x);
366 sub[0].fY = SkDoubleToScalar(dst[0].y);
367 sub[1].fX = SkDoubleToScalar(dst[1].x);
368 sub[1].fY = SkDoubleToScalar(dst[1].y);
369 sub[2].fX = SkDoubleToScalar(dst[2].x);
370 sub[2].fY = SkDoubleToScalar(dst[2].y);
371 sub[3].fX = SkDoubleToScalar(dst[3].x);
372 sub[3].fY = SkDoubleToScalar(dst[3].y);
373}
374
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000375static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
376 SkPoint []) = {
377 NULL,
378 LineSubDivide,
379 QuadSubDivide,
380 CubicSubDivide
381};
382
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000383static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000384 _Line sub) {
385 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000386 _Line dst;
387 sub_divide(aLine, startT, endT, dst);
388 sub[0] = dst[0];
389 sub[1] = dst[1];
390}
391
392static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000393 Quadratic sub) {
394 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000395 Quadratic dst;
396 sub_divide(aQuad, startT, endT, dst);
397 sub[0] = dst[0];
398 sub[1] = dst[1];
399 sub[2] = dst[2];
400}
401
402static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000403 Cubic sub) {
404 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000405 Cubic dst;
406 sub_divide(aCubic, startT, endT, dst);
407 sub[0] = dst[0];
408 sub[1] = dst[1];
409 sub[2] = dst[2];
410 sub[3] = dst[3];
411}
412
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000413#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000414static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
415 SkRect& bounds) {
416 SkPoint dst[3];
417 QuadSubDivide(a, startT, endT, dst);
418 bounds.fLeft = bounds.fRight = dst[0].fX;
419 bounds.fTop = bounds.fBottom = dst[0].fY;
420 for (int index = 1; index < 3; ++index) {
421 bounds.growToInclude(dst[index].fX, dst[index].fY);
422 }
423}
424
425static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
426 SkRect& bounds) {
427 SkPoint dst[4];
428 CubicSubDivide(a, startT, endT, dst);
429 bounds.fLeft = bounds.fRight = dst[0].fX;
430 bounds.fTop = bounds.fBottom = dst[0].fY;
431 for (int index = 1; index < 4; ++index) {
432 bounds.growToInclude(dst[index].fX, dst[index].fY);
433 }
434}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000435#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000436
caryclark@google.com15fa1382012-05-07 20:49:36 +0000437static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000438 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000439 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000440 Quadratic dst;
441 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000442 if (order == 2) { // quad became line
443 for (int index = 0; index < order; ++index) {
444 SkPoint* pt = reducePts.append();
445 pt->fX = SkDoubleToScalar(dst[index].x);
446 pt->fY = SkDoubleToScalar(dst[index].y);
447 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000448 }
449 return (SkPath::Verb) (order - 1);
450}
451
452static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
453 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000454 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000455 Cubic dst;
456 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000457 if (order == 2 || order == 3) { // cubic became line or quad
458 for (int index = 0; index < order; ++index) {
459 SkPoint* pt = reducePts.append();
460 pt->fX = SkDoubleToScalar(dst[index].x);
461 pt->fY = SkDoubleToScalar(dst[index].y);
462 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000463 }
464 return (SkPath::Verb) (order - 1);
465}
466
caryclark@google.com15fa1382012-05-07 20:49:36 +0000467static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000468 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000469 return isLinear(aQuad, 0, 2);
470}
471
472static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000473 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000474 return isLinear(aCubic, 0, 3);
475}
476
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000477static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000478 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000479 double x[2];
480 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000481 xy_at_t(aLine, endT, x[1], *(double*) 0);
482 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000483}
484
485static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000486 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000487 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000488}
489
490static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000491 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000492 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000493}
494
495static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
496 NULL,
497 LineLeftMost,
498 QuadLeftMost,
499 CubicLeftMost
500};
501
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000502#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000503static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
504 Intersections& intersections) {
505 MAKE_CONST_QUAD(aQuad, a);
506 MAKE_CONST_LINE(bLine, b);
507 return intersectRay(aQuad, bLine, intersections);
508}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000509#endif
510
511static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
512 Intersections& intersections) {
513 MAKE_CONST_QUAD(aQuad, a);
514 return intersectRay(aQuad, bLine, intersections);
515}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000516
caryclark@google.comdb0b3e02012-12-21 21:34:36 +0000517static bool LineVertical(const SkPoint a[2], double startT, double endT) {
518 MAKE_CONST_LINE(aLine, a);
519 double x[2];
520 xy_at_t(aLine, startT, x[0], *(double*) 0);
521 xy_at_t(aLine, endT, x[1], *(double*) 0);
522 return approximately_equal((float) x[0], (float) x[1]);
523}
524
525static bool QuadVertical(const SkPoint a[3], double startT, double endT) {
526 SkPoint dst[3];
527 QuadSubDivide(a, startT, endT, dst);
528 return approximately_equal(dst[0].fX, dst[1].fX) && approximately_equal(dst[1].fX, dst[2].fX);
529}
530
531static bool CubicVertical(const SkPoint a[4], double startT, double endT) {
532 SkPoint dst[4];
533 CubicSubDivide(a, startT, endT, dst);
534 return approximately_equal(dst[0].fX, dst[1].fX) && approximately_equal(dst[1].fX, dst[2].fX)
535 && approximately_equal(dst[2].fX, dst[3].fX);
536}
537
538static bool (* const SegmentVertical[])(const SkPoint [], double , double) = {
539 NULL,
540 LineVertical,
541 QuadVertical,
542 CubicVertical
543};
544
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000545class Segment;
546
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000547struct Span {
548 Segment* fOther;
549 mutable SkPoint fPt; // lazily computed as needed
550 double fT;
551 double fOtherT; // value at fOther[fOtherIndex].fT
552 int fOtherIndex; // can't be used during intersection
caryclark@google.com31143cf2012-11-09 22:14:19 +0000553 int fWindSum; // accumulated from contours surrounding this one.
554 int fOppSum; // for binary operators: the opposite winding sum
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000555 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000556 int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000557 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000558 bool fUnsortableStart; // set when start is part of an unsortable pair
559 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.comf839c032012-10-26 21:03:50 +0000560 bool fTiny; // if set, span may still be considered once for edge following
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000561};
562
caryclark@google.com15fa1382012-05-07 20:49:36 +0000563// sorting angles
564// given angles of {dx dy ddx ddy dddx dddy} sort them
565class Angle {
566public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000567 // FIXME: this is bogus for quads and cubics
568 // if the quads and cubics' line from end pt to ctrl pt are coincident,
569 // there's no obvious way to determine the curve ordering from the
570 // derivatives alone. In particular, if one quadratic's coincident tangent
571 // is longer than the other curve, the final control point can place the
572 // longer curve on either side of the shorter one.
573 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
574 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000575
caryclark@google.com32546db2012-08-31 20:55:07 +0000576 /*(
577 for quads and cubics, set up a parameterized line (e.g. LineParameters )
578 for points [0] to [1]. See if point [2] is on that line, or on one side
579 or the other. If it both quads' end points are on the same side, choose
580 the shorter tangent. If the tangents are equal, choose the better second
581 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000582
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000583 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000584 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000585 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000586 double y = dy();
587 double ry = rh.dy();
588 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
589 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000590 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000591 double x = dx();
592 double rx = rh.dx();
593 if (y == 0 && ry == 0 && x * rx < 0) {
594 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000595 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000596 double x_ry = x * ry;
597 double rx_y = rx * y;
598 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000599 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000600 return cmp < 0;
601 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000602 if (approximately_zero(x_ry) && approximately_zero(rx_y)
603 && !approximately_zero_squared(cmp)) {
604 return cmp < 0;
605 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000606 // at this point, the initial tangent line is coincident
caryclark@google.com31143cf2012-11-09 22:14:19 +0000607 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
608 || !approximately_zero(rh.fSide))) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000609 // FIXME: running demo will trigger this assertion
610 // (don't know if commenting out will trigger further assertion or not)
611 // commenting it out allows demo to run in release, though
612 // SkASSERT(fSide != rh.fSide);
613 return fSide < rh.fSide;
614 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000615 // see if either curve can be lengthened and try the tangent compare again
616 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
617 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
618 Angle longer = *this;
619 Angle rhLonger = rh;
620 if (longer.lengthen() | rhLonger.lengthen()) {
621 return longer < rhLonger;
622 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000623 // what if we extend in the other direction?
624 longer = *this;
625 rhLonger = rh;
626 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
627 return longer < rhLonger;
628 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000629 }
caryclark@google.com185c7c42012-10-19 18:26:24 +0000630 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
caryclark@google.com31143cf2012-11-09 22:14:19 +0000631 || (rh.fVerb == SkPath::kLine_Verb
632 && approximately_zero(rx) && approximately_zero(ry))) {
caryclark@google.com185c7c42012-10-19 18:26:24 +0000633 // See general unsortable comment below. This case can happen when
634 // one line has a non-zero change in t but no change in x and y.
635 fUnsortable = true;
636 rh.fUnsortable = true;
637 return this < &rh; // even with no solution, return a stable sort
638 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000639 if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny
640 || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) {
641 fUnsortable = true;
642 rh.fUnsortable = true;
643 return this < &rh; // even with no solution, return a stable sort
644 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000645 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
646 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000647 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000648 // end of the shorter tangent to midway between the end points
649 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000650 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
651 double len = fTangent1.normalSquared();
652 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000653 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000654 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000655 int roots, rroots;
656 bool flip = false;
657 do {
658 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
659 double midX = (q[0].x + q[2].x) / 2;
660 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000661 ray[0] = q[1];
662 ray[1].x = midX;
663 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000664 SkASSERT(ray[0] != ray[1]);
665 roots = QuadRayIntersect(fPts, ray, i);
666 rroots = QuadRayIntersect(rh.fPts, ray, ri);
667 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000668 if (roots == 0 || rroots == 0) {
669 // FIXME: we don't have a solution in this case. The interim solution
670 // is to mark the edges as unsortable, exclude them from this and
671 // future computations, and allow the returned path to be fragmented
672 fUnsortable = true;
673 rh.fUnsortable = true;
674 return this < &rh; // even with no solution, return a stable sort
675 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000676 _Point loc;
677 double best = SK_ScalarInfinity;
678 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000679 int index;
680 for (index = 0; index < roots; ++index) {
681 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000682 dx = loc.x - ray[0].x;
683 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000684 dist = dx * dx + dy * dy;
685 if (best > dist) {
686 best = dist;
687 }
688 }
689 for (index = 0; index < rroots; ++index) {
690 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000691 dx = loc.x - ray[0].x;
692 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000693 dist = dx * dx + dy * dy;
694 if (best > dist) {
695 return fSide < 0;
696 }
697 }
698 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000699 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000700
caryclark@google.com47580692012-07-23 12:14:49 +0000701 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000702 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000703 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000704
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000705 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000706 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000707 }
708
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000709 int end() const {
710 return fEnd;
711 }
712
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000713 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000714 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000715 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000716
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000717 bool lengthen() {
718 int newEnd = fEnd;
719 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
720 fEnd = newEnd;
721 setSpans();
722 return true;
723 }
724 return false;
725 }
726
caryclark@google.coma461ff02012-10-11 12:54:23 +0000727 bool reverseLengthen() {
728 if (fReversed) {
729 return false;
730 }
731 int newEnd = fStart;
732 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
733 fEnd = newEnd;
734 fReversed = true;
735 setSpans();
736 return true;
737 }
738 return false;
739 }
740
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000741 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000742 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000743 fSegment = segment;
744 fStart = start;
745 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000746 fPts = orig;
747 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000748 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000749 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000750 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000751 setSpans();
752 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000753
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000754 void setSpans() {
755 double startT = (*fSpans)[fStart].fT;
756 double endT = (*fSpans)[fEnd].fT;
757 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000758 case SkPath::kLine_Verb:
759 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000760 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000761 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
762 fTangent1.lineEndPoints(l);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000763 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000764 break;
765 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000766 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000767 fTangent1.quadEndPoints(fQ, 0, 1);
768 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000769 break;
770 case SkPath::kCubic_Verb:
771 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000772 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000773 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000774 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000775 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000776 default:
777 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000778 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +0000779 fUnsortable = dx() == 0 && dy() == 0;
caryclark@google.comf839c032012-10-26 21:03:50 +0000780 if (fUnsortable) {
781 return;
782 }
783 SkASSERT(fStart != fEnd);
784 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
785 for (int index = fStart; index != fEnd; index += step) {
786 if ((*fSpans)[index].fUnsortableStart) {
787 fUnsortable = true;
788 return;
789 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000790#if 0
caryclark@google.comf839c032012-10-26 21:03:50 +0000791 if (index != fStart && (*fSpans)[index].fUnsortableEnd) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000792 SkASSERT(0);
caryclark@google.comf839c032012-10-26 21:03:50 +0000793 fUnsortable = true;
794 return;
795 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000796#endif
caryclark@google.comf839c032012-10-26 21:03:50 +0000797 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000798 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000799
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000800 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000801 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000802 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000803
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000804 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000805 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000806 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000807
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000808 const SkTDArray<Span>* spans() const {
809 return fSpans;
810 }
811
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000812 int start() const {
813 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000814 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000815
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000816 bool unsortable() const {
817 return fUnsortable;
818 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000819
caryclark@google.comc899ad92012-08-23 15:24:42 +0000820#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000821 const SkPoint* pts() const {
822 return fPts;
823 }
824
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000825 SkPath::Verb verb() const {
826 return fVerb;
827 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000828
caryclark@google.comc899ad92012-08-23 15:24:42 +0000829 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000830 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000831 }
832#endif
833
caryclark@google.com15fa1382012-05-07 20:49:36 +0000834private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000835 const SkPoint* fPts;
836 Quadratic fQ;
837 SkPath::Verb fVerb;
838 double fSide;
839 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000840 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000841 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000842 int fStart;
843 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000844 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000845 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000846};
847
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000848// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000849struct Bounds : public SkRect {
850 static bool Intersects(const Bounds& a, const Bounds& b) {
851 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
852 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
853 }
854
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000855 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
856 if (left < fLeft) {
857 fLeft = left;
858 }
859 if (top < fTop) {
860 fTop = top;
861 }
862 if (right > fRight) {
863 fRight = right;
864 }
865 if (bottom > fBottom) {
866 fBottom = bottom;
867 }
868 }
869
870 void add(const Bounds& toAdd) {
871 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
872 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000873
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000874 void add(const SkPoint& pt) {
875 if (pt.fX < fLeft) fLeft = pt.fX;
876 if (pt.fY < fTop) fTop = pt.fY;
877 if (pt.fX > fRight) fRight = pt.fX;
878 if (pt.fY > fBottom) fBottom = pt.fY;
879 }
880
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000881 bool isEmpty() {
882 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000883 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000884 || isnan(fLeft) || isnan(fRight)
885 || isnan(fTop) || isnan(fBottom);
886 }
887
888 void setCubicBounds(const SkPoint a[4]) {
889 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000890 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000891 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000892 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
893 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000894 }
895
896 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000897 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000898 _Rect dRect;
899 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000900 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
901 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000902 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000903
904 void setPoint(const SkPoint& pt) {
905 fLeft = fRight = pt.fX;
906 fTop = fBottom = pt.fY;
907 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000908};
909
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000910// OPTIMIZATION: does the following also work, and is it any faster?
911// return outerWinding * innerWinding > 0
912// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
caryclark@google.com2ddff932012-08-07 21:25:27 +0000913static bool useInnerWinding(int outerWinding, int innerWinding) {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000914 // SkASSERT(outerWinding != innerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +0000915 int absOut = abs(outerWinding);
916 int absIn = abs(innerWinding);
917 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
918 if (outerWinding * innerWinding < 0) {
919#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000920 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000921 outerWinding, innerWinding, result ? "true" : "false");
922#endif
923 }
924 return result;
925}
926
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000927#define F (false) // discard the edge
928#define T (true) // keep the edge
929
caryclark@google.comd0deb4f2012-12-17 13:58:08 +0000930static const bool gUnaryActiveEdge[2][2] = {
931// from=0 from=1
932// to=0,1 to=0,1
933 {F, T}, {T, F},
934};
935
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000936static const bool gActiveEdge[kShapeOp_Count][2][2][2][2] = {
937// miFrom=0 miFrom=1
938// miTo=0 miTo=1 miTo=0 miTo=1
939// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
940// suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1
941 {{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su
942 {{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su
943 {{{{F, T}, {T, F}}, {{T, T}, {F, F}}}, {{{T, F}, {T, F}}, {{F, F}, {F, F}}}}, // mi | su
944 {{{{F, T}, {T, F}}, {{T, F}, {F, T}}}, {{{T, F}, {F, T}}, {{F, T}, {T, F}}}}, // mi ^ su
caryclark@google.com235f56a2012-09-14 14:19:30 +0000945};
946
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000947#undef F
948#undef T
caryclark@google.com235f56a2012-09-14 14:19:30 +0000949
caryclark@google.comf839c032012-10-26 21:03:50 +0000950// wrap path to keep track of whether the contour is initialized and non-empty
951class PathWrapper {
952public:
953 PathWrapper(SkPath& path)
954 : fPathPtr(&path)
caryclark@google.comd0deb4f2012-12-17 13:58:08 +0000955 , fCloses(0)
956 , fMoves(0)
caryclark@google.comf839c032012-10-26 21:03:50 +0000957 {
958 init();
959 }
960
961 void close() {
962 if (!fHasMove) {
963 return;
964 }
965 bool callClose = isClosed();
966 lineTo();
967 if (fEmpty) {
968 return;
969 }
970 if (callClose) {
971 #if DEBUG_PATH_CONSTRUCTION
972 SkDebugf("path.close();\n");
973 #endif
974 fPathPtr->close();
caryclark@google.comd0deb4f2012-12-17 13:58:08 +0000975 fCloses++;
caryclark@google.comf839c032012-10-26 21:03:50 +0000976 }
977 init();
978 }
979
980 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
981 lineTo();
982 moveTo();
983#if DEBUG_PATH_CONSTRUCTION
984 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
985 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
986#endif
987 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
988 fDefer[0] = fDefer[1] = pt3;
989 fEmpty = false;
990 }
991
992 void deferredLine(const SkPoint& pt) {
993 if (pt == fDefer[1]) {
994 return;
995 }
996 if (changedSlopes(pt)) {
997 lineTo();
998 fDefer[0] = fDefer[1];
999 }
1000 fDefer[1] = pt;
1001 }
1002
1003 void deferredMove(const SkPoint& pt) {
1004 fMoved = true;
1005 fHasMove = true;
1006 fEmpty = true;
1007 fDefer[0] = fDefer[1] = pt;
1008 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001009
caryclark@google.comf839c032012-10-26 21:03:50 +00001010 void deferredMoveLine(const SkPoint& pt) {
1011 if (!fHasMove) {
1012 deferredMove(pt);
1013 }
1014 deferredLine(pt);
1015 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001016
caryclark@google.comf839c032012-10-26 21:03:50 +00001017 bool hasMove() const {
1018 return fHasMove;
1019 }
1020
1021 void init() {
1022 fEmpty = true;
1023 fHasMove = false;
1024 fMoved = false;
1025 }
1026
1027 bool isClosed() const {
1028 return !fEmpty && fFirstPt == fDefer[1];
1029 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001030
caryclark@google.comf839c032012-10-26 21:03:50 +00001031 void lineTo() {
1032 if (fDefer[0] == fDefer[1]) {
1033 return;
1034 }
1035 moveTo();
1036 fEmpty = false;
1037#if DEBUG_PATH_CONSTRUCTION
1038 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
1039#endif
1040 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
1041 fDefer[0] = fDefer[1];
1042 }
1043
1044 const SkPath* nativePath() const {
1045 return fPathPtr;
1046 }
1047
1048 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
1049 lineTo();
1050 moveTo();
1051#if DEBUG_PATH_CONSTRUCTION
1052 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
1053 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1054#endif
1055 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1056 fDefer[0] = fDefer[1] = pt2;
1057 fEmpty = false;
1058 }
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +00001059
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001060 bool someAssemblyRequired() const {
1061 return fCloses < fMoves;
1062 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001063
1064protected:
1065 bool changedSlopes(const SkPoint& pt) const {
1066 if (fDefer[0] == fDefer[1]) {
1067 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001068 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001069 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
1070 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
1071 SkScalar lineDx = pt.fX - fDefer[1].fX;
1072 SkScalar lineDy = pt.fY - fDefer[1].fY;
1073 return deferDx * lineDy != deferDy * lineDx;
1074 }
1075
1076 void moveTo() {
1077 if (!fMoved) {
1078 return;
1079 }
1080 fFirstPt = fDefer[0];
1081#if DEBUG_PATH_CONSTRUCTION
1082 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
1083#endif
1084 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
1085 fMoved = false;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001086 fMoves++;
caryclark@google.comf839c032012-10-26 21:03:50 +00001087 }
1088
1089private:
1090 SkPath* fPathPtr;
1091 SkPoint fDefer[2];
1092 SkPoint fFirstPt;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001093 int fCloses;
1094 int fMoves;
caryclark@google.comf839c032012-10-26 21:03:50 +00001095 bool fEmpty;
1096 bool fHasMove;
1097 bool fMoved;
1098};
1099
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001100class Segment {
1101public:
1102 Segment() {
1103#if DEBUG_DUMP
1104 fID = ++gSegmentID;
1105#endif
1106 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001107
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001108 bool operator<(const Segment& rh) const {
1109 return fBounds.fTop < rh.fBounds.fTop;
1110 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001111
caryclark@google.com4eeda372012-12-06 21:47:48 +00001112 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001113 if (activeAngleInner(index, done, angles)) {
1114 return true;
1115 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001116 int lesser = index;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001117 while (--lesser >= 0 && equalPoints(index, lesser)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001118 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001119 return true;
1120 }
1121 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001122 lesser = index;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001123 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001124 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001125 return true;
1126 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001127 } while (++index < fTs.count() && equalPoints(index, lesser));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001128 return false;
1129 }
1130
caryclark@google.com4eeda372012-12-06 21:47:48 +00001131 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001132 Span* span = &fTs[index];
1133 Segment* other = span->fOther;
1134 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001135 return other->activeAngleInner(oIndex, done, angles);
1136 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001137
caryclark@google.com4eeda372012-12-06 21:47:48 +00001138 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001139 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001140 if (next > 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001141 Span& upSpan = fTs[index];
1142 if (upSpan.fWindValue || upSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001143 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001144 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001145 done++;
1146 } else if (upSpan.fWindSum != SK_MinS32) {
1147 return true;
1148 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001149 } else if (!upSpan.fDone) {
1150 upSpan.fDone = true;
1151 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001152 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001153 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001154 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001155 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001156 if (prev >= 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001157 Span& downSpan = fTs[prev];
1158 if (downSpan.fWindValue || downSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001159 addAngle(angles, index, prev);
1160 if (downSpan.fDone) {
1161 done++;
1162 } else if (downSpan.fWindSum != SK_MinS32) {
1163 return true;
1164 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001165 } else if (!downSpan.fDone) {
1166 downSpan.fDone = true;
1167 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001168 }
1169 }
1170 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001171 }
1172
caryclark@google.comf839c032012-10-26 21:03:50 +00001173 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001174 SkASSERT(!done());
1175 int count = fTs.count();
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001176 result.fX = result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001177 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001178 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001179 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001180 const Span& span = fTs[index];
1181 if (span.fUnsortableStart | lastUnsortable) {
1182 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001183 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001184 if (!span.fDone | !lastDone) {
1185 const SkPoint& xy = xyAtT(index);
1186 if (result.fY < xy.fY) {
1187 goto next;
1188 }
1189 if (result.fY == xy.fY && result.fX < xy.fX) {
1190 goto next;
1191 }
1192 result = xy;
1193 }
1194 next:
1195 lastDone = span.fDone;
1196 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001197 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001198 }
1199
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001200 bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, ShapeOp op) {
1201 int sumMiWinding = updateWinding(endIndex, index);
1202 int sumSuWinding = updateOppWinding(endIndex, index);
1203 if (fOperand) {
1204 SkTSwap<int>(sumMiWinding, sumSuWinding);
1205 }
1206 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
1207 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, sumMiWinding, sumSuWinding,
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001208 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001209 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001210
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001211 bool activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, ShapeOp op,
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001212 int& sumMiWinding, int& sumSuWinding,
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001213 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
1214 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
1215 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001216 bool miFrom;
1217 bool miTo;
1218 bool suFrom;
1219 bool suTo;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001220 if (operand()) {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001221 miFrom = (oppMaxWinding & xorMiMask) != 0;
1222 miTo = (oppSumWinding & xorMiMask) != 0;
1223 suFrom = (maxWinding & xorSuMask) != 0;
1224 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001225 } else {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001226 miFrom = (maxWinding & xorMiMask) != 0;
1227 miTo = (sumWinding & xorMiMask) != 0;
1228 suFrom = (oppMaxWinding & xorSuMask) != 0;
1229 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001230 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001231 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
1232 SkASSERT(result != -1);
1233 return result;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001234 }
1235
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001236 bool activeWinding(int index, int endIndex) {
1237 int sumWinding = updateWinding(endIndex, index);
1238 int maxWinding;
1239 return activeWinding(index, endIndex, maxWinding, sumWinding);
1240 }
1241
1242 bool activeWinding(int index, int endIndex, int& maxWinding, int& sumWinding) {
1243 setUpWinding(index, endIndex, maxWinding, sumWinding);
1244 bool from = maxWinding != 0;
1245 bool to = sumWinding != 0;
1246 bool result = gUnaryActiveEdge[from][to];
1247 SkASSERT(result != -1);
1248 return result;
1249 }
1250
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001251 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001252 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001253 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001254#if DEBUG_ANGLE
caryclark@google.com31143cf2012-11-09 22:14:19 +00001255 if (angles.count() > 1 && !fTs[start].fTiny) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001256 SkPoint angle0Pt, newPt;
1257 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1258 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1259 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1260 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1261 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1262 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001263#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001264 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001265 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001266
caryclark@google.com2ddff932012-08-07 21:25:27 +00001267 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001268 double oEnd) {
1269 int tIndex = -1;
1270 int tCount = fTs.count();
1271 int oIndex = -1;
1272 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001273 do {
1274 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001275 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001276 int tIndexStart = tIndex;
1277 do {
1278 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001279 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001280 int oIndexStart = oIndex;
1281 double nextT;
1282 do {
1283 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001284 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001285 double oNextT;
1286 do {
1287 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001288 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001289 // at this point, spans before and after are at:
1290 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1291 // if tIndexStart == 0, no prior span
1292 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001293
caryclark@google.comcc905052012-07-25 20:59:42 +00001294 // advance the span with zero winding
1295 // if the following span exists (not past the end, non-zero winding)
1296 // connect the two edges
1297 if (!fTs[tIndexStart].fWindValue) {
1298 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1299 #if DEBUG_CONCIDENT
1300 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1301 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001302 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1303 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001304 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001305 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001306 }
1307 if (nextT < 1 && fTs[tIndex].fWindValue) {
1308 #if DEBUG_CONCIDENT
1309 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1310 __FUNCTION__, fID, other.fID, tIndex,
1311 fTs[tIndex].fT, xyAtT(tIndex).fX,
1312 xyAtT(tIndex).fY);
1313 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001314 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001315 }
1316 } else {
1317 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1318 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1319 #if DEBUG_CONCIDENT
1320 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1321 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001322 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1323 other.xyAtT(oIndexStart).fY);
1324 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001325 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001326 }
1327 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1328 #if DEBUG_CONCIDENT
1329 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1330 __FUNCTION__, fID, other.fID, oIndex,
1331 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1332 other.xyAtT(oIndex).fY);
1333 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1334 #endif
1335 }
1336 }
1337 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001338
caryclark@google.comcc905052012-07-25 20:59:42 +00001339 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1340 double oEnd) {
1341 // walk this to outsideTs[0]
1342 // walk other to outsideTs[1]
1343 // if either is > 0, add a pointer to the other, copying adjacent winding
1344 int tIndex = -1;
1345 int oIndex = -1;
1346 double tStart = outsideTs[0];
1347 double oStart = outsideTs[1];
1348 do {
1349 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001350 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001351 do {
1352 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001353 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001354 if (tIndex > 0 || oIndex > 0 || fOperand != other.fOperand) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001355 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001356 }
1357 tStart = fTs[tIndex].fT;
1358 oStart = other.fTs[oIndex].fT;
1359 do {
1360 double nextT;
1361 do {
1362 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001363 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001364 tStart = nextT;
1365 do {
1366 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001367 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001368 oStart = nextT;
caryclark@google.com4eeda372012-12-06 21:47:48 +00001369 if (tStart == 1 && oStart == 1 && fOperand == other.fOperand) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001370 break;
1371 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001372 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001373 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001374 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001375
caryclark@google.com4eeda372012-12-06 21:47:48 +00001376 void addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
1377 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001378 fBounds.setCubicBounds(pts);
1379 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001380
caryclark@google.comf839c032012-10-26 21:03:50 +00001381 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001382 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001383 const SkPoint* ePtr;
1384 int lastT = fTs.count() - 1;
1385 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1386 ePtr = fPts;
1387 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001388 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001389 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1390 ePtr = edge;
1391 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001392 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001393 bool reverse = ePtr == fPts && start != 0;
1394 if (reverse) {
1395 path.deferredMoveLine(ePtr[fVerb]);
1396 switch (fVerb) {
1397 case SkPath::kLine_Verb:
1398 path.deferredLine(ePtr[0]);
1399 break;
1400 case SkPath::kQuad_Verb:
1401 path.quadTo(ePtr[1], ePtr[0]);
1402 break;
1403 case SkPath::kCubic_Verb:
1404 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1405 break;
1406 default:
1407 SkASSERT(0);
1408 }
1409 // return ePtr[0];
1410 } else {
1411 path.deferredMoveLine(ePtr[0]);
1412 switch (fVerb) {
1413 case SkPath::kLine_Verb:
1414 path.deferredLine(ePtr[1]);
1415 break;
1416 case SkPath::kQuad_Verb:
1417 path.quadTo(ePtr[1], ePtr[2]);
1418 break;
1419 case SkPath::kCubic_Verb:
1420 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1421 break;
1422 default:
1423 SkASSERT(0);
1424 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001425 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001426 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001427 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001428 }
1429
caryclark@google.com4eeda372012-12-06 21:47:48 +00001430 void addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
1431 init(pts, SkPath::kLine_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001432 fBounds.set(pts, 2);
1433 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001434
caryclark@google.comf839c032012-10-26 21:03:50 +00001435#if 0
1436 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001437 const SkPoint& pt = xyAtT(tIndex);
1438 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001439 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001440 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001441 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001442 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001443#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001444
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001445 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001446 void addOtherT(int index, double otherT, int otherIndex) {
1447 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001448 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001449 if (precisely_less_than_zero(otherT)) {
1450 otherT = 0;
1451 } else if (precisely_greater_than_one(otherT)) {
1452 otherT = 1;
1453 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001454 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001455 span.fOtherT = otherT;
1456 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001457 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001458
caryclark@google.com4eeda372012-12-06 21:47:48 +00001459 void addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
1460 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001461 fBounds.setQuadBounds(pts);
1462 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001463
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001464 // Defer all coincident edge processing until
1465 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001466
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001467// no need to be tricky; insert in normal T order
1468// resolve overlapping ts when considering coincidence later
1469
1470 // add non-coincident intersection. Resulting edges are sorted in T.
1471 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001472 // FIXME: in the pathological case where there is a ton of intercepts,
1473 // binary search?
1474 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001475 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001476 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001477 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001478 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001479 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001480 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001481 newT = 1;
1482 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001483 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001484 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001485 // OPTIMIZATION: if there are three or more identical Ts, then
1486 // the fourth and following could be further insertion-sorted so
1487 // that all the edges are clockwise or counterclockwise.
1488 // This could later limit segment tests to the two adjacent
1489 // neighbors, although it doesn't help with determining which
1490 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001491 if (newT < fTs[index].fT) {
1492 insertedAt = index;
1493 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001494 }
1495 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001496 Span* span;
1497 if (insertedAt >= 0) {
1498 span = fTs.insert(insertedAt);
1499 } else {
1500 insertedAt = tCount;
1501 span = fTs.append();
1502 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001503 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001504 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001505 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001506 span->fWindSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001507 span->fOppSum = SK_MinS32;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001508 span->fWindValue = 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001509 span->fOppValue = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001510 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001511 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001512 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001513 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001514 span->fUnsortableStart = false;
1515 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001516 if (span - fTs.begin() > 0 && !span[-1].fDone
1517 && !precisely_negative(newT - span[-1].fT)
1518 // && approximately_negative(newT - span[-1].fT)
1519 && xyAtT(&span[-1]) == xyAtT(span)) {
1520 span[-1].fTiny = true;
1521 span[-1].fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001522 if (approximately_negative(newT - span[-1].fT)) {
1523 if (approximately_greater_than_one(newT)) {
1524 span[-1].fUnsortableStart = true;
1525 span[-2].fUnsortableEnd = true;
1526 }
1527 if (approximately_less_than_zero(span[-1].fT)) {
1528 span->fUnsortableStart = true;
1529 span[-1].fUnsortableEnd = true;
1530 }
1531 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001532 ++fDoneSpans;
1533 }
1534 if (fTs.end() - span > 1 && !span->fDone
1535 && !precisely_negative(span[1].fT - newT)
1536 // && approximately_negative(span[1].fT - newT)
1537 && xyAtT(&span[1]) == xyAtT(span)) {
1538 span->fTiny = true;
1539 span->fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001540 if (approximately_negative(span[1].fT - newT)) {
1541 if (approximately_greater_than_one(span[1].fT)) {
1542 span->fUnsortableStart = true;
1543 span[-1].fUnsortableEnd = true;
1544 }
1545 if (approximately_less_than_zero(newT)) {
1546 span[1].fUnsortableStart = true;
1547 span->fUnsortableEnd = true;
1548 }
1549 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001550 ++fDoneSpans;
1551 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001552 return insertedAt;
1553 }
1554
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001555 // set spans from start to end to decrement by one
1556 // note this walks other backwards
1557 // FIMXE: there's probably an edge case that can be constructed where
1558 // two span in one segment are separated by float epsilon on one span but
1559 // not the other, if one segment is very small. For this
1560 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001561 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001562 // sorted? It feels better in such a case to match the span's other span
1563 // pointer since both coincident segments must contain the same spans.
1564 void addTCancel(double startT, double endT, Segment& other,
1565 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001566 SkASSERT(!approximately_negative(endT - startT));
1567 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001568 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001569 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001570 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001571 ++index;
1572 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001573 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001574 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001575 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001576 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001577 Span* test = &fTs[index];
1578 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001579 SkTDArray<double> outsideTs;
1580 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001581 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001582 bool decrement = test->fWindValue && oTest->fWindValue && !binary;
caryclark@google.comcc905052012-07-25 20:59:42 +00001583 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001584 double testT = test->fT;
1585 double oTestT = oTest->fT;
1586 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001587 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001588 if (decrement) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001589 decrementSpan(span);
caryclark@google.com200c2112012-08-03 15:05:04 +00001590 } else if (track && span->fT < 1 && oTestT < 1) {
1591 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001592 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001593 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001594 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001595 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001596 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1597 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1598 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001599 while (approximately_negative(otherTMatchStart - oSpan->fT)
1600 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001601 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001602 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001603 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001604 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001605 other.decrementSpan(oSpan);
1606 } else if (track && oSpan->fT < 1 && testT < 1) {
1607 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001608 }
1609 if (!oIndex) {
1610 break;
1611 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001612 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001613 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001614 test = span;
1615 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001616 } while (!approximately_negative(endT - test->fT));
1617 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001618 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001619 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001620 double tStart = outsideTs[0];
1621 double oStart = outsideTs[1];
1622 addCancelOutsides(tStart, oStart, other, oEndT);
1623 int count = outsideTs.count();
1624 if (count > 2) {
1625 double tStart = outsideTs[count - 2];
1626 double oStart = outsideTs[count - 1];
1627 addCancelOutsides(tStart, oStart, other, oEndT);
1628 }
caryclark@google.com18063442012-07-25 12:05:18 +00001629 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001630 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001631 double tStart = oOutsideTs[0];
1632 double oStart = oOutsideTs[1];
1633 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001634 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001635 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00001636
caryclark@google.com4eeda372012-12-06 21:47:48 +00001637 int bumpCoincidentThis(const Span* oTest, bool opp, int index,
1638 SkTDArray<double>& outsideTs) {
1639 int oWindValue = oTest->fWindValue;
1640 int oOppValue = oTest->fOppValue;
1641 if (opp) {
1642 SkTSwap<int>(oWindValue, oOppValue);
1643 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001644 Span* const test = &fTs[index];
1645 Span* end = test;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001646 const double oStartT = oTest->fT;
1647 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001648 if (bumpSpan(end, oWindValue, oOppValue)) {
1649 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001650 }
1651 end = &fTs[++index];
1652 } while (approximately_negative(end->fT - test->fT));
1653 return index;
1654 }
1655
1656 // because of the order in which coincidences are resolved, this and other
1657 // may not have the same intermediate points. Compute the corresponding
1658 // intermediate T values (using this as the master, other as the follower)
1659 // and walk other conditionally -- hoping that it catches up in the end
caryclark@google.com4eeda372012-12-06 21:47:48 +00001660 int bumpCoincidentOther(const Span* test, double oEndT, int& oIndex,
1661 SkTDArray<double>& oOutsideTs) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001662 Span* const oTest = &fTs[oIndex];
1663 Span* oEnd = oTest;
1664 const double startT = test->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001665 const double oStartT = oTest->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001666 while (!approximately_negative(oEndT - oEnd->fT)
caryclark@google.com4eeda372012-12-06 21:47:48 +00001667 && approximately_negative(oEnd->fT - oStartT)) {
1668 zeroSpan(oEnd);
1669 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001670 oEnd = &fTs[++oIndex];
1671 }
1672 return oIndex;
1673 }
1674
1675 // FIXME: need to test this case:
1676 // contourA has two segments that are coincident
1677 // contourB has two segments that are coincident in the same place
1678 // each ends up with +2/0 pairs for winding count
1679 // since logic below doesn't transfer count (only increments/decrements) can this be
1680 // resolved to +4/0 ?
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001681
1682 // set spans from start to end to increment the greater by one and decrement
1683 // the lesser
caryclark@google.com4eeda372012-12-06 21:47:48 +00001684 void addTCoincident(double startT, double endT, Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001685 SkASSERT(!approximately_negative(endT - startT));
1686 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001687 bool opp = fOperand ^ other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001688 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001689 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001690 ++index;
1691 }
1692 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001693 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001694 ++oIndex;
1695 }
1696 Span* test = &fTs[index];
1697 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001698 SkTDArray<double> outsideTs;
1699 SkTDArray<double> oOutsideTs;
1700 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001701 // if either span has an opposite value and the operands don't match, resolve first
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001702 // SkASSERT(!test->fDone || !oTest->fDone);
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001703 if (test->fDone || oTest->fDone) {
1704 index = advanceCoincidentThis(oTest, opp, index);
1705 oIndex = other.advanceCoincidentOther(test, oEndT, oIndex);
1706 } else {
1707 index = bumpCoincidentThis(oTest, opp, index, outsideTs);
1708 oIndex = other.bumpCoincidentOther(test, oEndT, oIndex, oOutsideTs);
1709 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001710 test = &fTs[index];
1711 oTest = &other.fTs[oIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001712 } while (!approximately_negative(endT - test->fT));
1713 SkASSERT(approximately_negative(oTest->fT - oEndT));
1714 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001715 if (!done() && outsideTs.count()) {
1716 addCoinOutsides(outsideTs, other, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001717 }
1718 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001719 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001720 }
1721 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001722
caryclark@google.comcc905052012-07-25 20:59:42 +00001723 // FIXME: this doesn't prevent the same span from being added twice
1724 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001725 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001726 int tCount = fTs.count();
1727 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1728 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001729 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001730 break;
1731 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001732 if (approximately_negative(span.fT - t) && span.fOther == &other
1733 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001734#if DEBUG_ADD_T_PAIR
1735 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1736 __FUNCTION__, fID, t, other.fID, otherT);
1737#endif
1738 return;
1739 }
1740 }
caryclark@google.com47580692012-07-23 12:14:49 +00001741#if DEBUG_ADD_T_PAIR
1742 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1743 __FUNCTION__, fID, t, other.fID, otherT);
1744#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001745 int insertedAt = addT(t, &other);
1746 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001747 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001748 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001749 matchWindingValue(insertedAt, t, borrowWind);
1750 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001751 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001752
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001753 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001754 // add edge leading into junction
caryclark@google.com4eeda372012-12-06 21:47:48 +00001755 int min = SkMin32(end, start);
1756 if (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001757 addAngle(angles, end, start);
1758 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001759 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001760 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001761 int tIndex = nextExactSpan(end, step);
caryclark@google.com4eeda372012-12-06 21:47:48 +00001762 min = SkMin32(end, tIndex);
1763 if (tIndex >= 0 && (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001764 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001765 }
1766 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001767
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001768 int advanceCoincidentThis(const Span* oTest, bool opp, int index) {
1769 Span* const test = &fTs[index];
1770 Span* end = test;
1771 do {
1772 end = &fTs[++index];
1773 } while (approximately_negative(end->fT - test->fT));
1774 return index;
1775 }
1776
1777 int advanceCoincidentOther(const Span* test, double oEndT, int& oIndex) {
1778 Span* const oTest = &fTs[oIndex];
1779 Span* oEnd = oTest;
1780 const double oStartT = oTest->fT;
1781 while (!approximately_negative(oEndT - oEnd->fT)
1782 && approximately_negative(oEnd->fT - oStartT)) {
1783 oEnd = &fTs[++oIndex];
1784 }
1785 return oIndex;
1786 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001787
1788 bool betweenTs(int lesser, double testT, int greater) {
1789 if (lesser > greater) {
1790 SkTSwap<int>(lesser, greater);
1791 }
1792 return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
1793 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001794
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001795 const Bounds& bounds() const {
1796 return fBounds;
1797 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001798
caryclark@google.com31143cf2012-11-09 22:14:19 +00001799 void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001800 double referenceT = fTs[index].fT;
1801 int lesser = index;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001802 while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand)
1803 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00001804 buildAnglesInner(lesser, angles);
1805 }
1806 do {
1807 buildAnglesInner(index, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001808 } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand)
1809 && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001810 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001811
1812 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1813 Span* span = &fTs[index];
1814 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001815 // if there is only one live crossing, and no coincidence, continue
1816 // in the same direction
1817 // if there is coincidence, the only choice may be to reverse direction
1818 // find edge on either side of intersection
1819 int oIndex = span->fOtherIndex;
1820 // if done == -1, prior span has already been processed
1821 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001822 int next = other->nextExactSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001823 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001824 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001825 next = other->nextExactSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001826 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001827 // add candidate into and away from junction
1828 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001829 }
1830
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001831 int computeSum(int startIndex, int endIndex, bool binary) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001832 SkTDArray<Angle> angles;
1833 addTwoAngles(startIndex, endIndex, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001834 buildAngles(endIndex, angles, false);
caryclark@google.comd1688742012-09-18 20:08:37 +00001835 // OPTIMIZATION: check all angles to see if any have computed wind sum
1836 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001837 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001838 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001839#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001840 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001841#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001842 if (!sortable) {
1843 return SK_MinS32;
1844 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001845 int angleCount = angles.count();
1846 const Angle* angle;
1847 const Segment* base;
1848 int winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001849 int oWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001850 int firstIndex = 0;
1851 do {
1852 angle = sorted[firstIndex];
1853 base = angle->segment();
1854 winding = base->windSum(angle);
1855 if (winding != SK_MinS32) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001856 oWinding = base->oppSum(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001857 break;
1858 }
1859 if (++firstIndex == angleCount) {
1860 return SK_MinS32;
1861 }
1862 } while (true);
1863 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001864 int spanWinding = base->spanSign(angle);
1865 bool inner = useInnerWinding(winding + spanWinding, winding);
1866 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001867 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001868 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001869 inner ? winding + spanWinding : winding);
1870 #endif
1871 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001872 winding += spanWinding;
1873 }
1874 #if DEBUG_SORT
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001875 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001876 #endif
1877 int nextIndex = firstIndex + 1;
1878 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001879 winding -= base->spanSign(angle);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001880 oWinding -= base->oppSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001881 do {
1882 if (nextIndex == angleCount) {
1883 nextIndex = 0;
1884 }
1885 angle = sorted[nextIndex];
1886 Segment* segment = angle->segment();
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001887 bool opp = base->fOperand ^ segment->fOperand;
1888 int maxWinding, oMaxWinding;
1889 int spanSign = segment->spanSign(angle);
1890 int oppoSign = segment->oppSign(angle);
1891 if (opp) {
1892 oMaxWinding = oWinding;
1893 oWinding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001894 maxWinding = winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001895 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001896 winding -= oppoSign;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001897 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001898 } else {
1899 maxWinding = winding;
1900 winding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001901 oMaxWinding = oWinding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001902 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001903 oWinding -= oppoSign;
1904 }
1905 }
1906 if (segment->windSum(angle) == SK_MinS32) {
1907 if (opp) {
1908 if (useInnerWinding(oMaxWinding, oWinding)) {
1909 oMaxWinding = oWinding;
1910 }
1911 if (oppoSign && useInnerWinding(maxWinding, winding)) {
1912 maxWinding = winding;
1913 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001914 (void) segment->markAndChaseWinding(angle, oMaxWinding, maxWinding);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001915 } else {
1916 if (useInnerWinding(maxWinding, winding)) {
1917 maxWinding = winding;
1918 }
1919 if (oppoSign && useInnerWinding(oMaxWinding, oWinding)) {
1920 oMaxWinding = oWinding;
1921 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001922 (void) segment->markAndChaseWinding(angle, maxWinding,
1923 binary ? oMaxWinding : 0);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001924 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001925 }
1926 } while (++nextIndex != lastIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001927 int minIndex = SkMin32(startIndex, endIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001928 return windSum(minIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001929 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001930
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001931 int crossedSpanX(const SkPoint& basePt, SkScalar& bestX, double& hitT,
1932 bool opp) const {
1933 SkScalar right = fBounds.fRight;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001934 int bestT = -1;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001935 if (right <= bestX) {
1936 return bestT;
1937 }
1938 SkScalar left = fBounds.fLeft;
1939 if (left >= basePt.fX) {
1940 return bestT;
1941 }
1942 if (fBounds.fTop > basePt.fY) {
1943 return bestT;
1944 }
1945 if (fBounds.fBottom < basePt.fY) {
1946 return bestT;
1947 }
1948 if (fBounds.fTop == fBounds.fBottom) {
1949 return bestT;
1950 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001951 int end = 0;
1952 do {
1953 int start = end;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001954 end = nextExactSpan(start, 1);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001955 if ((opp ? fTs[start].fOppValue : fTs[start].fWindValue) == 0) {
1956 continue;
1957 }
1958 SkPoint edge[4];
1959 double startT = fTs[start].fT;
1960 double endT = fTs[end].fT;
1961 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
1962 // intersect ray starting at basePt with edge
1963 Intersections intersections;
1964 // FIXME: always use original and limit results to T values within
1965 // start t and end t.
1966 // OPTIMIZE: use specialty function that intersects ray with curve,
1967 // returning t values only for curve (we don't care about t on ray)
1968 int pts = (*HSegmentIntersect[fVerb])(edge, left, right, basePt.fY,
1969 false, intersections);
1970 if (pts == 0) {
1971 continue;
1972 }
1973 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1974 // if the intersection is edge on, wait for another one
1975 continue;
1976 }
1977 for (int index = 0; index < pts; ++index) {
1978 double foundT = intersections.fT[0][index];
1979 double testT = startT + (endT - startT) * foundT;
1980 SkScalar testX = (*SegmentXAtT[fVerb])(fPts, testT);
1981 if (bestX < testX && testX < basePt.fX) {
1982 if (fVerb > SkPath::kLine_Verb
1983 && !approximately_less_than_zero(foundT)
1984 && !approximately_greater_than_one(foundT)) {
1985 SkScalar dy = (*SegmentDYAtT[fVerb])(fPts, testT);
1986 if (approximately_zero(dy)) {
1987 continue;
1988 }
1989 }
1990 bestX = testX;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001991 while (start + 1 < end && fTs[start].fDone) {
1992 ++start;
1993 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001994 bestT = foundT < 1 ? start : end;
1995 hitT = testT;
1996 }
1997 }
1998 } while (fTs[end].fT != 1);
1999 return bestT;
2000 }
2001
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002002 int crossedSpanY(const SkPoint& basePt, SkScalar& bestY, double& hitT,
2003 bool opp) const {
2004 SkScalar bottom = fBounds.fBottom;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002005 int bestT = -1;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002006 if (bottom <= bestY) {
2007 return bestT;
2008 }
2009 SkScalar top = fBounds.fTop;
2010 if (top >= basePt.fY) {
2011 return bestT;
2012 }
2013 if (fBounds.fLeft > basePt.fX) {
2014 return bestT;
2015 }
2016 if (fBounds.fRight < basePt.fX) {
2017 return bestT;
2018 }
2019 if (fBounds.fLeft == fBounds.fRight) {
2020 return bestT;
2021 }
caryclark@google.com210acaf2012-07-12 21:05:13 +00002022 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002023 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00002024 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002025 end = nextSpan(start, 1);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002026 if ((opp ? fTs[start].fOppValue : fTs[start].fWindValue) == 0) {
caryclark@google.com47580692012-07-23 12:14:49 +00002027 continue;
2028 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002029 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00002030 double startT = fTs[start].fT;
2031 double endT = fTs[end].fT;
2032 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002033 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002034 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00002035 // FIXME: always use original and limit results to T values within
2036 // start t and end t.
2037 // OPTIMIZE: use specialty function that intersects ray with curve,
2038 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002039 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
2040 false, intersections);
2041 if (pts == 0) {
2042 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002043 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002044 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
2045 // if the intersection is edge on, wait for another one
2046 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002047 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002048 for (int index = 0; index < pts; ++index) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002049 double foundT = intersections.fT[0][index];
2050 double testT = startT + (endT - startT) * foundT;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002051 SkScalar testY = (*SegmentYAtT[fVerb])(fPts, testT);
2052 if (bestY < testY && testY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00002053 if (fVerb > SkPath::kLine_Verb
2054 && !approximately_less_than_zero(foundT)
2055 && !approximately_greater_than_one(foundT)) {
2056 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
2057 if (approximately_zero(dx)) {
2058 continue;
2059 }
2060 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002061 bestY = testY;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002062 while (start + 1 < end && fTs[start].fDone) {
2063 ++start;
2064 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002065 bestT = foundT < 1 ? start : end;
2066 hitT = testT;
2067 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002068 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002069 } while (fTs[end].fT != 1);
2070 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002071 }
caryclark@google.com18063442012-07-25 12:05:18 +00002072
caryclark@google.com4eeda372012-12-06 21:47:48 +00002073 void decrementSpan(Span* span) {
caryclark@google.com18063442012-07-25 12:05:18 +00002074 SkASSERT(span->fWindValue > 0);
2075 if (--(span->fWindValue) == 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00002076 if (!span->fOppValue && !span->fDone) {
caryclark@google.comf839c032012-10-26 21:03:50 +00002077 span->fDone = true;
2078 ++fDoneSpans;
2079 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002080 }
2081 }
2082
2083 bool bumpSpan(Span* span, int windDelta, int oppDelta) {
2084 SkASSERT(!span->fDone);
2085 span->fWindValue += windDelta;
2086 SkASSERT(span->fWindValue >= 0);
2087 span->fOppValue += oppDelta;
2088 SkASSERT(span->fOppValue >= 0);
2089 if (fXor) {
2090 span->fWindValue &= 1;
2091 }
2092 if (fOppXor) {
2093 span->fOppValue &= 1;
2094 }
2095 if (!span->fWindValue && !span->fOppValue) {
2096 span->fDone = true;
2097 ++fDoneSpans;
caryclark@google.com18063442012-07-25 12:05:18 +00002098 return true;
2099 }
2100 return false;
2101 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002102
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00002103 // OPTIMIZE
2104 // when the edges are initially walked, they don't automatically get the prior and next
2105 // edges assigned to positions t=0 and t=1. Doing that would remove the need for this check,
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002106 // and would additionally remove the need for similar checks in condition edges. It would
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00002107 // also allow intersection code to assume end of segment intersections (maybe?)
2108 bool complete() const {
2109 int count = fTs.count();
2110 return count > 1 && fTs[0].fT == 0 && fTs[--count].fT == 1;
2111 }
caryclark@google.com18063442012-07-25 12:05:18 +00002112
caryclark@google.com15fa1382012-05-07 20:49:36 +00002113 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002114 SkASSERT(fDoneSpans <= fTs.count());
2115 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002116 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002117
caryclark@google.comf839c032012-10-26 21:03:50 +00002118 bool done(int min) const {
2119 return fTs[min].fDone;
2120 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002121
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002122 bool done(const Angle* angle) const {
2123 return done(SkMin32(angle->start(), angle->end()));
caryclark@google.com47580692012-07-23 12:14:49 +00002124 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002125
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002126 bool equalPoints(int greaterTIndex, int lesserTIndex) {
2127 SkASSERT(greaterTIndex >= lesserTIndex);
2128 double greaterT = fTs[greaterTIndex].fT;
2129 double lesserT = fTs[lesserTIndex].fT;
2130 if (greaterT == lesserT) {
2131 return true;
2132 }
2133 if (!approximately_negative(greaterT - lesserT)) {
2134 return false;
2135 }
2136 return xyAtT(greaterTIndex) == xyAtT(lesserTIndex);
2137 }
2138
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002139 /*
2140 The M and S variable name parts stand for the operators.
2141 Mi stands for Minuend (see wiki subtraction, analogous to difference)
2142 Su stands for Subtrahend
2143 The Opp variable name part designates that the value is for the Opposite operator.
2144 Opposite values result from combining coincident spans.
2145 */
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002146
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002147 Segment* findNextOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
2148 bool& unsortable, ShapeOp op, const int xorMiMask, const int xorSuMask) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002149 const int startIndex = nextStart;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002150 const int endIndex = nextEnd;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002151 SkASSERT(startIndex != endIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002152 const int count = fTs.count();
2153 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
2154 const int step = SkSign32(endIndex - startIndex);
2155 const int end = nextExactSpan(startIndex, step);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002156 SkASSERT(end >= 0);
2157 Span* endSpan = &fTs[end];
2158 Segment* other;
2159 if (isSimple(end)) {
2160 // mark the smaller of startIndex, endIndex done, and all adjacent
2161 // spans with the same T value (but not 'other' spans)
2162 #if DEBUG_WINDING
2163 SkDebugf("%s simple\n", __FUNCTION__);
2164 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002165 int min = SkMin32(startIndex, endIndex);
2166 if (fTs[min].fDone) {
2167 return NULL;
2168 }
2169 markDoneBinary(min);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002170 other = endSpan->fOther;
2171 nextStart = endSpan->fOtherIndex;
2172 double startT = other->fTs[nextStart].fT;
2173 nextEnd = nextStart;
2174 do {
2175 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002176 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002177 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002178 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2179 return other;
2180 }
2181 // more than one viable candidate -- measure angles to find best
2182 SkTDArray<Angle> angles;
2183 SkASSERT(startIndex - endIndex != 0);
2184 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2185 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002186 buildAngles(end, angles, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002187 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002188 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002189 int angleCount = angles.count();
2190 int firstIndex = findStartingEdge(sorted, startIndex, end);
2191 SkASSERT(firstIndex >= 0);
2192 #if DEBUG_SORT
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002193 debugShowSort(__FUNCTION__, sorted, firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002194 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002195 if (!sortable) {
2196 unsortable = true;
2197 return NULL;
2198 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002199 SkASSERT(sorted[firstIndex]->segment() == this);
2200 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002201 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
2202 sorted[firstIndex]->sign());
caryclark@google.com235f56a2012-09-14 14:19:30 +00002203 #endif
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002204 int sumMiWinding = updateWinding(endIndex, startIndex);
2205 int sumSuWinding = updateOppWinding(endIndex, startIndex);
2206 if (operand()) {
2207 SkTSwap<int>(sumMiWinding, sumSuWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002208 }
2209 int nextIndex = firstIndex + 1;
2210 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2211 const Angle* foundAngle = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002212 bool foundDone = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002213 // iterate through the angle, and compute everyone's winding
caryclark@google.com235f56a2012-09-14 14:19:30 +00002214 Segment* nextSegment;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002215 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002216 SkASSERT(nextIndex != firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002217 if (nextIndex == angleCount) {
2218 nextIndex = 0;
2219 }
2220 const Angle* nextAngle = sorted[nextIndex];
2221 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002222 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
2223 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
2224 nextAngle->end(), op, sumMiWinding, sumSuWinding,
2225 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
2226 if (activeAngle && (!foundAngle || foundDone)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002227 foundAngle = nextAngle;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002228 foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002229 }
2230 if (nextSegment->done()) {
2231 continue;
2232 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002233 if (nextSegment->windSum(nextAngle) != SK_MinS32) {
2234 continue;
2235 }
2236 Span* last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding,
2237 oppSumWinding, activeAngle, nextAngle);
2238 if (last) {
2239 *chase.append() = last;
2240#if DEBUG_WINDING
2241 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2242 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2243#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002244 }
2245 } while (++nextIndex != lastIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002246 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002247 if (!foundAngle) {
2248 return NULL;
2249 }
2250 nextStart = foundAngle->start();
2251 nextEnd = foundAngle->end();
2252 nextSegment = foundAngle->segment();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002253
caryclark@google.com235f56a2012-09-14 14:19:30 +00002254 #if DEBUG_WINDING
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002255 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2256 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002257 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002258 return nextSegment;
2259 }
caryclark@google.com47580692012-07-23 12:14:49 +00002260
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002261 // so the span needs to contain the pairing info found here
2262 // this should include the winding computed for the edge, and
2263 // what edge it connects to, and whether it is discarded
2264 // (maybe discarded == abs(winding) > 1) ?
2265 // only need derivatives for duration of sorting, add a new struct
2266 // for pairings, remove extra spans that have zero length and
2267 // reference an unused other
2268 // for coincident, the last span on the other may be marked done
2269 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002270
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002271 // if loop is exhausted, contour may be closed.
2272 // FIXME: pass in close point so we can check for closure
2273
2274 // given a segment, and a sense of where 'inside' is, return the next
2275 // segment. If this segment has an intersection, or ends in multiple
2276 // segments, find the mate that continues the outside.
2277 // note that if there are multiples, but no coincidence, we can limit
2278 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002279
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002280 // mark found segments as done
2281
caryclark@google.com15fa1382012-05-07 20:49:36 +00002282 // start is the index of the beginning T of this edge
2283 // it is guaranteed to have an end which describes a non-zero length (?)
2284 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002285 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002286 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2287 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002288 const int startIndex = nextStart;
2289 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002290 int outerWinding = winding;
2291 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002292 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002293 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2294 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002295 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002296 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002297 outerWinding = innerWinding;
2298 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002299 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002300 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002301 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2302 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002303 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002304 int end = nextExactSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002305 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002306 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002307 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002308 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002309 // mark the smaller of startIndex, endIndex done, and all adjacent
2310 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002311 #if DEBUG_WINDING
2312 SkDebugf("%s simple\n", __FUNCTION__);
2313 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002314 int min = SkMin32(startIndex, endIndex);
2315 if (fTs[min].fDone) {
2316 return NULL;
2317 }
2318 markDone(min, outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002319 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002320 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002321 double startT = other->fTs[nextStart].fT;
2322 nextEnd = nextStart;
2323 do {
2324 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002325 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002326 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00002327 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002328 return other;
2329 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002330 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002331 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002332 SkASSERT(startIndex - endIndex != 0);
2333 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002334 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002335 buildAngles(end, angles, false);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002336 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002337 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002338 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002339 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002340 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002341 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002342 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002343 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002344 if (!sortable) {
2345 unsortable = true;
2346 return NULL;
2347 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002348 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002349 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002350 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002351 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002352 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002353 int nextIndex = firstIndex + 1;
2354 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2355 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002356 // FIXME: found done logic probably fails if there are more than 4
2357 // sorted angles. It should bias towards the first and last undone
2358 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002359 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002360 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002361 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002362 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002363 bool altFlipped = false;
2364 bool foundFlipped = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002365 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002366 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002367 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002368 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002369 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002370 nextIndex = 0;
2371 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002372 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002373 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002374 if (sumWinding) {
2375 lastNonZeroSum = sumWinding;
2376 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002377 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002378 bool nextDone = nextSegment->done(nextAngle);
2379 bool nextTiny = nextSegment->tiny(nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002380 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002381 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002382 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002383 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002384 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2385 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002386 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002387 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002388 if (!active) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002389 // FIXME: shouldn't this call mark and chase done ?
caryclark@google.com59823f72012-08-09 18:17:47 +00002390 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002391 // FIXME: shouldn't this call mark and chase winding ?
caryclark@google.com47580692012-07-23 12:14:49 +00002392 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002393 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002394 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002395 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002396 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002397 return NULL;
2398 }
caryclark@google.com47580692012-07-23 12:14:49 +00002399 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002400 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002401 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002402 foundFlipped = altFlipped;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002403 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002404 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002405 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002406
caryclark@google.com24bec792012-08-20 12:43:57 +00002407 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002408 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002409 if (foundAngle && foundDone2) {
2410 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002411 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002412 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002413 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002414 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002415 foundFlipped = altFlipped;
2416 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002417 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002418 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002419 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002420 }
2421 // if the winding is non-zero, nextAngle does not connect to
2422 // current chain. If we haven't done so already, mark the angle
2423 // as done, record the winding value, and mark connected unambiguous
2424 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002425 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002426 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002427 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002428 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002429 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002430 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002431 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002432 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002433 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002434 }
2435 if (last) {
2436 *chase.append() = last;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002437 #if DEBUG_WINDING
2438 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2439 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2440 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002441 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002442 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002443 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002444 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002445 if (!foundAngle) {
2446 return NULL;
2447 }
2448 nextStart = foundAngle->start();
2449 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002450 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002451 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002452 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2453 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002454 if (winding) {
2455 #if DEBUG_WINDING
2456 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2457 if (foundSum == SK_MinS32) {
2458 SkDebugf("?");
2459 } else {
2460 SkDebugf("%d", foundSum);
2461 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002462 SkDebugf("\n");
2463 #endif
2464 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002465 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002466 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002467 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002468 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002469 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002470 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002471
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002472 Segment* findNextWinding(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
2473 bool& unsortable) {
2474 const int startIndex = nextStart;
2475 const int endIndex = nextEnd;
2476 SkASSERT(startIndex != endIndex);
2477 const int count = fTs.count();
2478 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
2479 const int step = SkSign32(endIndex - startIndex);
2480 const int end = nextExactSpan(startIndex, step);
2481 SkASSERT(end >= 0);
2482 Span* endSpan = &fTs[end];
2483 Segment* other;
2484 if (isSimple(end)) {
2485 // mark the smaller of startIndex, endIndex done, and all adjacent
2486 // spans with the same T value (but not 'other' spans)
2487 #if DEBUG_WINDING
2488 SkDebugf("%s simple\n", __FUNCTION__);
2489 #endif
2490 int min = SkMin32(startIndex, endIndex);
2491 if (fTs[min].fDone) {
2492 return NULL;
2493 }
2494 markDoneUnary(min);
2495 other = endSpan->fOther;
2496 nextStart = endSpan->fOtherIndex;
2497 double startT = other->fTs[nextStart].fT;
2498 nextEnd = nextStart;
2499 do {
2500 nextEnd += step;
2501 }
2502 while (precisely_zero(startT - other->fTs[nextEnd].fT));
2503 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2504 return other;
2505 }
2506 // more than one viable candidate -- measure angles to find best
2507 SkTDArray<Angle> angles;
2508 SkASSERT(startIndex - endIndex != 0);
2509 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2510 addTwoAngles(startIndex, end, angles);
2511 buildAngles(end, angles, true);
2512 SkTDArray<Angle*> sorted;
2513 bool sortable = SortAngles(angles, sorted);
2514 int angleCount = angles.count();
2515 int firstIndex = findStartingEdge(sorted, startIndex, end);
2516 SkASSERT(firstIndex >= 0);
2517 #if DEBUG_SORT
2518 debugShowSort(__FUNCTION__, sorted, firstIndex);
2519 #endif
2520 if (!sortable) {
2521 unsortable = true;
2522 return NULL;
2523 }
2524 SkASSERT(sorted[firstIndex]->segment() == this);
2525 #if DEBUG_WINDING
2526 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
2527 sorted[firstIndex]->sign());
2528 #endif
2529 int sumWinding = updateWinding(endIndex, startIndex);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002530 int nextIndex = firstIndex + 1;
2531 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2532 const Angle* foundAngle = NULL;
2533 bool foundDone = false;
2534 // iterate through the angle, and compute everyone's winding
2535 Segment* nextSegment;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002536 int activeCount = 0;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002537 do {
2538 SkASSERT(nextIndex != firstIndex);
2539 if (nextIndex == angleCount) {
2540 nextIndex = 0;
2541 }
2542 const Angle* nextAngle = sorted[nextIndex];
2543 nextSegment = nextAngle->segment();
2544 int maxWinding;
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +00002545 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002546 maxWinding, sumWinding);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002547 if (activeAngle) {
2548 ++activeCount;
2549 if (!foundAngle || (foundDone && activeCount & 1)) {
2550 if (nextSegment->tiny(nextAngle)) {
2551 unsortable = true;
2552 return NULL;
2553 }
2554 foundAngle = nextAngle;
2555 foundDone = nextSegment->done(nextAngle);
2556 }
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002557 }
2558 if (nextSegment->done()) {
2559 continue;
2560 }
2561 if (nextSegment->windSum(nextAngle) != SK_MinS32) {
2562 continue;
2563 }
2564 Span* last = nextSegment->markAngle(maxWinding, sumWinding, activeAngle, nextAngle);
2565 if (last) {
2566 *chase.append() = last;
2567#if DEBUG_WINDING
2568 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2569 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2570#endif
2571 }
2572 } while (++nextIndex != lastIndex);
2573 markDoneUnary(SkMin32(startIndex, endIndex));
2574 if (!foundAngle) {
2575 return NULL;
2576 }
2577 nextStart = foundAngle->start();
2578 nextEnd = foundAngle->end();
2579 nextSegment = foundAngle->segment();
2580
2581 #if DEBUG_WINDING
2582 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2583 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
2584 #endif
2585 return nextSegment;
2586 }
2587
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002588 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002589 const int startIndex = nextStart;
2590 const int endIndex = nextEnd;
2591 SkASSERT(startIndex != endIndex);
2592 int count = fTs.count();
2593 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2594 : startIndex > 0);
2595 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002596 int end = nextExactSpan(startIndex, step);
caryclark@google.com24bec792012-08-20 12:43:57 +00002597 SkASSERT(end >= 0);
2598 Span* endSpan = &fTs[end];
2599 Segment* other;
caryclark@google.com24bec792012-08-20 12:43:57 +00002600 if (isSimple(end)) {
2601 #if DEBUG_WINDING
2602 SkDebugf("%s simple\n", __FUNCTION__);
2603 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002604 int min = SkMin32(startIndex, endIndex);
2605 if (fTs[min].fDone) {
2606 return NULL;
2607 }
2608 markDone(min, 1);
caryclark@google.com24bec792012-08-20 12:43:57 +00002609 other = endSpan->fOther;
2610 nextStart = endSpan->fOtherIndex;
2611 double startT = other->fTs[nextStart].fT;
2612 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002613 if ((approximately_less_than_zero(startT) && step < 0)
2614 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002615 step = -step;
2616 SkDEBUGCODE(firstLoop = false;)
2617 }
2618 do {
2619 nextEnd = nextStart;
2620 do {
2621 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002622 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002623 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002624 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2625 break;
2626 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002627 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002628 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002629 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002630 SkDEBUGCODE(firstLoop = false;)
2631 step = -step;
2632 } while (true);
2633 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2634 return other;
2635 }
2636 SkTDArray<Angle> angles;
2637 SkASSERT(startIndex - endIndex != 0);
2638 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2639 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002640 buildAngles(end, angles, false);
caryclark@google.com24bec792012-08-20 12:43:57 +00002641 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002642 bool sortable = SortAngles(angles, sorted);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002643 if (!sortable) {
2644 unsortable = true;
2645 return NULL;
2646 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002647 int angleCount = angles.count();
2648 int firstIndex = findStartingEdge(sorted, startIndex, end);
2649 SkASSERT(firstIndex >= 0);
2650 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002651 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002652 #endif
2653 SkASSERT(sorted[firstIndex]->segment() == this);
2654 int nextIndex = firstIndex + 1;
2655 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2656 const Angle* nextAngle;
2657 Segment* nextSegment;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002658 bool foundAngle = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002659 do {
2660 if (nextIndex == angleCount) {
2661 nextIndex = 0;
2662 }
2663 nextAngle = sorted[nextIndex];
2664 nextSegment = nextAngle->segment();
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002665 if (!nextSegment->done(nextAngle) || nextSegment->tiny(nextAngle)) {
2666 foundAngle = true;
caryclark@google.com24bec792012-08-20 12:43:57 +00002667 break;
2668 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002669 } while (++nextIndex != lastIndex);
2670 markDone(SkMin32(startIndex, endIndex), 1);
2671 if (!foundAngle) {
2672 nextIndex = firstIndex + 1 == angleCount ? 0 : firstIndex + 1;
2673 nextAngle = sorted[nextIndex];
2674 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002675 nextStart = nextAngle->start();
2676 nextEnd = nextAngle->end();
2677 return nextSegment;
2678 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002679
2680 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2681 int angleCount = sorted.count();
2682 int firstIndex = -1;
2683 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2684 const Angle* angle = sorted[angleIndex];
2685 if (angle->segment() == this && angle->start() == end &&
2686 angle->end() == start) {
2687 firstIndex = angleIndex;
2688 break;
2689 }
2690 }
2691 return firstIndex;
2692 }
2693
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002694 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com4eeda372012-12-06 21:47:48 +00002695 void findTooCloseToCall() {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002696 int count = fTs.count();
2697 if (count < 3) { // require t=0, x, 1 at minimum
2698 return;
2699 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002700 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002701 int moCount;
2702 Span* match;
2703 Segment* mOther;
2704 do {
2705 match = &fTs[matchIndex];
2706 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002707 // FIXME: allow quads, cubics to be near coincident?
2708 if (mOther->fVerb == SkPath::kLine_Verb) {
2709 moCount = mOther->fTs.count();
2710 if (moCount >= 3) {
2711 break;
2712 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002713 }
2714 if (++matchIndex >= count) {
2715 return;
2716 }
2717 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002718 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002719 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002720 // look for a pair of nearby T values that map to the same (x,y) value
2721 // if found, see if the pair of other segments share a common point. If
2722 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002723 for (int index = matchIndex + 1; index < count; ++index) {
2724 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002725 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002726 continue;
2727 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002728 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002729 if (tOther->fVerb != SkPath::kLine_Verb) {
2730 continue; // FIXME: allow quads, cubics to be near coincident?
2731 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002732 int toCount = tOther->fTs.count();
2733 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002734 continue;
2735 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002736 const SkPoint* testPt = &xyAtT(test);
2737 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002738 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002739 moCount = toCount;
2740 match = test;
2741 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002742 matchPt = testPt;
2743 continue;
2744 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002745 int moStart = -1;
2746 int moEnd = -1;
2747 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002748 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002749 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002750 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002751 continue;
2752 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002753 if (moSpan.fOther == this) {
2754 if (moSpan.fOtherT == match->fT) {
2755 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002756 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002757 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002758 continue;
2759 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002760 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002761 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2762 moStart = -1;
2763 break;
2764 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002765 SkASSERT(moEnd == -1);
2766 moEnd = moIndex;
2767 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002768 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002769 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002770 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002771 continue;
2772 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002773 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002774 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002775 continue;
2776 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002777 int toStart = -1;
2778 int toEnd = -1;
2779 double toStartT, toEndT;
2780 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2781 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002782 if (toSpan.fDone) {
2783 continue;
2784 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002785 if (toSpan.fOther == this) {
2786 if (toSpan.fOtherT == test->fT) {
2787 toStart = toIndex;
2788 toStartT = toSpan.fT;
2789 }
2790 continue;
2791 }
2792 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002793 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2794 moStart = -1;
2795 break;
2796 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002797 SkASSERT(toEnd == -1);
2798 toEnd = toIndex;
2799 toEndT = toSpan.fT;
2800 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002801 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002802 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2803 if (toStart <= 0 || toEnd <= 0) {
2804 continue;
2805 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002806 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002807 continue;
2808 }
2809 // test to see if the segment between there and here is linear
2810 if (!mOther->isLinear(moStart, moEnd)
2811 || !tOther->isLinear(toStart, toEnd)) {
2812 continue;
2813 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002814 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002815 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002816 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002817 } else {
caryclark@google.com4eeda372012-12-06 21:47:48 +00002818 mOther->addTCoincident(moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002819 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002820 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002821 }
2822
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002823 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002824 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002825 // a) mark spans with either end unsortable as done, or
2826 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2827 // when encountering an unsortable span
2828
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002829 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2830 // and use more concise logic like the old edge walker code?
2831 // FIXME: this needs to deal with coincident edges
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002832 Segment* findTop(int& tIndex, int& endIndex, bool& unsortable, bool onlySortable) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002833 // iterate through T intersections and return topmost
2834 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002835 SkASSERT(!done());
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002836 int firstT = -1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002837 SkPoint topPt;
2838 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002839 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002840 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002841 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002842 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002843 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002844 const Span& span = fTs[index];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002845 if (onlySortable && (span.fUnsortableStart || lastUnsortable)) {
caryclark@google.comf839c032012-10-26 21:03:50 +00002846 goto next;
2847 }
2848 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002849 const SkPoint& intercept = xyAtT(&span);
2850 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2851 && topPt.fX > intercept.fX)) {
2852 topPt = intercept;
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002853 firstT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002854 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002855 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002856 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002857 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002858 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002859 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002860 SkASSERT(firstT >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002861 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002862 int step = 1;
2863 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002864 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002865 step = -1;
2866 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002867 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002868 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002869 // if the topmost T is not on end, or is three-way or more, find left
2870 // look for left-ness from tLeft to firstT (matching y of other)
2871 SkTDArray<Angle> angles;
2872 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002873 addTwoAngles(end, firstT, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002874 buildAngles(firstT, angles, true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002875 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002876 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002877 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002878 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002879 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002880 if (onlySortable && !sortable) {
2881 unsortable = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002882 return NULL;
2883 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002884 // skip edges that have already been processed
2885 firstT = -1;
2886 Segment* leftSegment;
2887 do {
2888 const Angle* angle = sorted[++firstT];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002889 SkASSERT(!onlySortable || !angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002890 leftSegment = angle->segment();
2891 tIndex = angle->end();
2892 endIndex = angle->start();
2893 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002894 SkASSERT(!leftSegment->fTs[SkMin32(tIndex, endIndex)].fTiny);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002895 return leftSegment;
2896 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002897
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002898 // FIXME: not crazy about this
2899 // when the intersections are performed, the other index is into an
2900 // incomplete array. as the array grows, the indices become incorrect
2901 // while the following fixes the indices up again, it isn't smart about
2902 // skipping segments whose indices are already correct
2903 // assuming we leave the code that wrote the index in the first place
2904 void fixOtherTIndex() {
2905 int iCount = fTs.count();
2906 for (int i = 0; i < iCount; ++i) {
2907 Span& iSpan = fTs[i];
2908 double oT = iSpan.fOtherT;
2909 Segment* other = iSpan.fOther;
2910 int oCount = other->fTs.count();
2911 for (int o = 0; o < oCount; ++o) {
2912 Span& oSpan = other->fTs[o];
2913 if (oT == oSpan.fT && this == oSpan.fOther) {
2914 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002915 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002916 }
2917 }
2918 }
2919 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002920
caryclark@google.com4eeda372012-12-06 21:47:48 +00002921 void init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002922 fDoneSpans = 0;
2923 fOperand = operand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00002924 fXor = evenOdd;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002925 fPts = pts;
2926 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002927 }
2928
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002929 void initWinding(int start, int end, bool checkInX, int winding, int oppWinding) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002930 int local = spanSign(start, end);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002931 if (checkInX && !winding) {
2932 winding = -local;
2933 } else if ((local * winding >= 0) ^ (checkInX && winding != 0)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002934 winding += local;
2935 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002936 int oppLocal = oppSign(start, end);
2937 if ((oppLocal * oppWinding >= 0) ^ (checkInX & oppWinding != 0)) {
2938 oppWinding += oppLocal;
2939 }
2940 (void) markAndChaseWinding(start, end, winding, oppWinding);
2941 }
2942
2943 void initWindingX(int start, int end, double tHit, int winding, int oppWinding) {
2944 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, tHit);
2945 SkASSERT(dx);
2946 int local = spanSign(start, end);
2947 if (!winding) {
2948 winding = dx < 0 ? -local : local;
2949 } else if (local * winding >= 0) {
2950 winding += local;
2951 }
2952 int oppLocal = oppSign(start, end);
2953 if (oppLocal * oppWinding >= 0) {
2954 oppWinding += oppLocal;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002955 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002956 (void) markAndChaseWinding(start, end, winding, oppWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002957 }
2958
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002959 bool intersected() const {
2960 return fTs.count() > 0;
2961 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002962
2963 bool isConnected(int startIndex, int endIndex) const {
2964 return fTs[startIndex].fWindSum != SK_MinS32
2965 || fTs[endIndex].fWindSum != SK_MinS32;
2966 }
2967
caryclark@google.com235f56a2012-09-14 14:19:30 +00002968 bool isHorizontal() const {
2969 return fBounds.fTop == fBounds.fBottom;
2970 }
2971
caryclark@google.com15fa1382012-05-07 20:49:36 +00002972 bool isLinear(int start, int end) const {
2973 if (fVerb == SkPath::kLine_Verb) {
2974 return true;
2975 }
2976 if (fVerb == SkPath::kQuad_Verb) {
2977 SkPoint qPart[3];
2978 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2979 return QuadIsLinear(qPart);
2980 } else {
2981 SkASSERT(fVerb == SkPath::kCubic_Verb);
2982 SkPoint cPart[4];
2983 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2984 return CubicIsLinear(cPart);
2985 }
2986 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002987
2988 // OPTIMIZE: successive calls could start were the last leaves off
2989 // or calls could specialize to walk forwards or backwards
2990 bool isMissing(double startT) const {
2991 size_t tCount = fTs.count();
2992 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002993 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002994 return false;
2995 }
2996 }
2997 return true;
2998 }
2999
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003000 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003001 int count = fTs.count();
3002 if (count == 2) {
3003 return true;
3004 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003005 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003006 if (approximately_less_than_zero(t)) {
3007 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003008 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003009 if (approximately_greater_than_one(t)) {
3010 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003011 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003012 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003013 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003014
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003015 bool isVertical() const {
3016 return fBounds.fLeft == fBounds.fRight;
3017 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003018
3019 bool isVertical(int start, int end) const {
3020 return (*SegmentVertical[fVerb])(fPts, start, end);
3021 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003022
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003023 SkScalar leftMost(int start, int end) const {
3024 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
3025 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003026
caryclark@google.com495f8e42012-05-31 13:13:11 +00003027 // this span is excluded by the winding rule -- chase the ends
3028 // as long as they are unambiguous to mark connections as done
3029 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00003030 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003031 int index = angle->start();
3032 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00003033 return markAndChaseDone(index, endIndex, winding);
3034 }
3035
caryclark@google.com31143cf2012-11-09 22:14:19 +00003036 Span* markAndChaseDone(int index, int endIndex, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003037 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003038 int min = SkMin32(index, endIndex);
3039 markDone(min, winding);
3040 Span* last;
3041 Segment* other = this;
3042 while ((other = other->nextChase(index, step, min, last))) {
3043 other->markDone(min, winding);
3044 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003045 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00003046 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003047
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003048 Span* markAndChaseDoneBinary(const Angle* angle, int winding, int oppWinding) {
3049 int index = angle->start();
3050 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00003051 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003052 int min = SkMin32(index, endIndex);
3053 markDoneBinary(min, winding, oppWinding);
3054 Span* last;
3055 Segment* other = this;
3056 while ((other = other->nextChase(index, step, min, last))) {
3057 other->markDoneBinary(min, winding, oppWinding);
3058 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003059 return last;
3060 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00003061
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003062 Span* markAndChaseDoneBinary(int index, int endIndex) {
3063 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003064 int min = SkMin32(index, endIndex);
3065 markDoneBinary(min);
3066 Span* last;
3067 Segment* other = this;
3068 while ((other = other->nextChase(index, step, min, last))) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00003069 if (other->done()) {
3070 return NULL;
3071 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003072 other->markDoneBinary(min);
3073 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003074 return last;
3075 }
3076
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003077 Span* markAndChaseDoneUnary(int index, int endIndex) {
3078 int step = SkSign32(endIndex - index);
3079 int min = SkMin32(index, endIndex);
3080 markDoneUnary(min);
3081 Span* last;
3082 Segment* other = this;
3083 while ((other = other->nextChase(index, step, min, last))) {
3084 if (other->done()) {
3085 return NULL;
3086 }
3087 other->markDoneUnary(min);
3088 }
3089 return last;
3090 }
3091
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003092 Span* markAndChaseDoneUnary(const Angle* angle, int winding) {
3093 int index = angle->start();
3094 int endIndex = angle->end();
3095 return markAndChaseDone(index, endIndex, winding);
3096 }
3097
caryclark@google.com4eeda372012-12-06 21:47:48 +00003098 Span* markAndChaseWinding(const Angle* angle, const int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003099 int index = angle->start();
3100 int endIndex = angle->end();
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00003101 int step = SkSign32(endIndex - index);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003102 int min = SkMin32(index, endIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00003103 markWinding(min, winding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003104 Span* last;
3105 Segment* other = this;
3106 while ((other = other->nextChase(index, step, min, last))) {
3107 if (other->fTs[min].fWindSum != SK_MinS32) {
3108 SkASSERT(other->fTs[min].fWindSum == winding);
3109 return NULL;
3110 }
3111 other->markWinding(min, winding);
3112 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003113 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003114 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00003115
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003116 Span* markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003117 int min = SkMin32(index, endIndex);
3118 int step = SkSign32(endIndex - index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003119 markWinding(min, winding, oppWinding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003120 Span* last;
3121 Segment* other = this;
3122 while ((other = other->nextChase(index, step, min, last))) {
3123 if (other->fTs[min].fWindSum != SK_MinS32) {
3124 SkASSERT(other->fTs[min].fWindSum == winding);
3125 return NULL;
3126 }
3127 other->markWinding(min, winding, oppWinding);
3128 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003129 return last;
3130 }
3131
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003132 Span* markAndChaseWinding(const Angle* angle, int winding, int oppWinding) {
3133 int start = angle->start();
3134 int end = angle->end();
3135 return markAndChaseWinding(start, end, winding, oppWinding);
3136 }
3137
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003138 Span* markAngle(int maxWinding, int sumWinding, bool activeAngle, const Angle* angle) {
3139 SkASSERT(angle->segment() == this);
3140 if (useInnerWinding(maxWinding, sumWinding)) {
3141 maxWinding = sumWinding;
3142 }
3143 Span* last;
3144 if (activeAngle) {
3145 last = markAndChaseWinding(angle, maxWinding);
3146 } else {
3147 last = markAndChaseDoneUnary(angle, maxWinding);
3148 }
3149 return last;
3150 }
3151
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003152 Span* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
3153 bool activeAngle, const Angle* angle) {
3154 SkASSERT(angle->segment() == this);
3155 if (useInnerWinding(maxWinding, sumWinding)) {
3156 maxWinding = sumWinding;
3157 }
3158 if (oppMaxWinding != oppSumWinding && useInnerWinding(oppMaxWinding, oppSumWinding)) {
3159 oppMaxWinding = oppSumWinding;
3160 }
3161 Span* last;
3162 if (activeAngle) {
3163 last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
3164 } else {
3165 last = markAndChaseDoneBinary(angle, maxWinding, oppMaxWinding);
3166 }
3167 return last;
3168 }
3169
caryclark@google.com495f8e42012-05-31 13:13:11 +00003170 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003171 // This may be called when the segment is already marked done. While this
3172 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00003173 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003174 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00003175 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003176 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003177 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003178 double referenceT = fTs[index].fT;
3179 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00003180 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3181 markOneDone(__FUNCTION__, lesser, winding);
3182 }
3183 do {
3184 markOneDone(__FUNCTION__, index, winding);
3185 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00003186 }
3187
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003188 void markDoneBinary(int index, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003189 // SkASSERT(!done());
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00003190 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003191 double referenceT = fTs[index].fT;
3192 int lesser = index;
3193 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003194 markOneDoneBinary(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003195 }
3196 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003197 markOneDoneBinary(__FUNCTION__, index, winding, oppWinding);
3198 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3199 }
3200
3201 void markDoneBinary(int index) {
3202 double referenceT = fTs[index].fT;
3203 int lesser = index;
3204 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3205 markOneDoneBinary(__FUNCTION__, lesser);
3206 }
3207 do {
3208 markOneDoneBinary(__FUNCTION__, index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003209 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003210 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003211
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003212 void markDoneUnary(int index, int winding) {
3213 // SkASSERT(!done());
3214 SkASSERT(winding);
3215 double referenceT = fTs[index].fT;
3216 int lesser = index;
3217 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3218 markOneDoneUnary(__FUNCTION__, lesser, winding);
3219 }
3220 do {
3221 markOneDoneUnary(__FUNCTION__, index, winding);
3222 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3223 }
3224
3225 void markDoneUnary(int index) {
3226 double referenceT = fTs[index].fT;
3227 int lesser = index;
3228 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3229 markOneDoneUnary(__FUNCTION__, lesser);
3230 }
3231 do {
3232 markOneDoneUnary(__FUNCTION__, index);
3233 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3234 }
3235
caryclark@google.com24bec792012-08-20 12:43:57 +00003236 void markOneDone(const char* funName, int tIndex, int winding) {
3237 Span* span = markOneWinding(funName, tIndex, winding);
3238 if (!span) {
3239 return;
3240 }
3241 span->fDone = true;
3242 fDoneSpans++;
3243 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003244
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003245 void markOneDoneBinary(const char* funName, int tIndex) {
3246 Span* span = verifyOneWinding(funName, tIndex);
3247 if (!span) {
3248 return;
3249 }
3250 span->fDone = true;
3251 fDoneSpans++;
3252 }
3253
3254 void markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003255 Span* span = markOneWinding(funName, tIndex, winding, oppWinding);
3256 if (!span) {
3257 return;
3258 }
3259 span->fDone = true;
3260 fDoneSpans++;
3261 }
3262
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003263 void markOneDoneUnary(const char* funName, int tIndex) {
3264 Span* span = verifyOneWindingU(funName, tIndex);
3265 if (!span) {
3266 return;
3267 }
3268 span->fDone = true;
3269 fDoneSpans++;
3270 }
3271
3272 void markOneDoneUnary(const char* funName, int tIndex, int winding) {
3273 Span* span = markOneWinding(funName, tIndex, winding);
3274 if (!span) {
3275 return;
3276 }
3277 span->fDone = true;
3278 fDoneSpans++;
3279 }
3280
caryclark@google.com24bec792012-08-20 12:43:57 +00003281 Span* markOneWinding(const char* funName, int tIndex, int winding) {
3282 Span& span = fTs[tIndex];
3283 if (span.fDone) {
3284 return NULL;
3285 }
3286 #if DEBUG_MARK_DONE
3287 debugShowNewWinding(funName, span, winding);
3288 #endif
3289 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00003290 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00003291 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00003292 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00003293 span.fWindSum = winding;
3294 return &span;
3295 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00003296
caryclark@google.com31143cf2012-11-09 22:14:19 +00003297 Span* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding) {
3298 Span& span = fTs[tIndex];
3299 if (span.fDone) {
3300 return NULL;
3301 }
3302 #if DEBUG_MARK_DONE
3303 debugShowNewWinding(funName, span, winding, oppWinding);
3304 #endif
3305 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
3306 #ifdef SK_DEBUG
3307 SkASSERT(abs(winding) <= gDebugMaxWindSum);
3308 #endif
3309 span.fWindSum = winding;
3310 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
3311 #ifdef SK_DEBUG
3312 SkASSERT(abs(oppWinding) <= gDebugMaxWindSum);
3313 #endif
3314 span.fOppSum = oppWinding;
3315 return &span;
3316 }
3317
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003318 Span* verifyOneWinding(const char* funName, int tIndex) {
3319 Span& span = fTs[tIndex];
3320 if (span.fDone) {
3321 return NULL;
3322 }
3323 #if DEBUG_MARK_DONE
3324 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
3325 #endif
3326 SkASSERT(span.fWindSum != SK_MinS32);
3327 SkASSERT(span.fOppSum != SK_MinS32);
3328 return &span;
3329 }
3330
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003331 Span* verifyOneWindingU(const char* funName, int tIndex) {
3332 Span& span = fTs[tIndex];
3333 if (span.fDone) {
3334 return NULL;
3335 }
3336 #if DEBUG_MARK_DONE
3337 debugShowNewWinding(funName, span, span.fWindSum);
3338 #endif
3339 SkASSERT(span.fWindSum != SK_MinS32);
3340 return &span;
3341 }
3342
caryclark@google.comf839c032012-10-26 21:03:50 +00003343 // note that just because a span has one end that is unsortable, that's
3344 // not enough to mark it done. The other end may be sortable, allowing the
3345 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003346 void markUnsortable(int start, int end) {
3347 Span* span = &fTs[start];
3348 if (start < end) {
3349 span->fUnsortableStart = true;
3350 } else {
3351 --span;
3352 span->fUnsortableEnd = true;
3353 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003354 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003355 return;
3356 }
3357 span->fDone = true;
3358 fDoneSpans++;
3359 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003360
caryclark@google.com59823f72012-08-09 18:17:47 +00003361 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003362 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003363 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003364 double referenceT = fTs[index].fT;
3365 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00003366 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3367 markOneWinding(__FUNCTION__, lesser, winding);
3368 }
3369 do {
3370 markOneWinding(__FUNCTION__, index, winding);
3371 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00003372 }
3373
3374 void markWinding(int index, int winding, int oppWinding) {
3375 // SkASSERT(!done());
caryclark@google.com4eeda372012-12-06 21:47:48 +00003376 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003377 double referenceT = fTs[index].fT;
3378 int lesser = index;
3379 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3380 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003381 }
3382 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003383 markOneWinding(__FUNCTION__, index, winding, oppWinding);
3384 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003385 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003386
caryclark@google.com2ddff932012-08-07 21:25:27 +00003387 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003388 int nextDoorWind = SK_MaxS32;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003389 int nextOppWind = SK_MaxS32;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003390 if (tIndex > 0) {
3391 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003392 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003393 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003394 nextOppWind = below.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003395 }
3396 }
3397 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3398 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003399 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003400 nextDoorWind = above.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003401 nextOppWind = above.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003402 }
3403 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003404 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
3405 const Span& below = fTs[tIndex - 1];
3406 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003407 nextOppWind = below.fOppValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003408 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003409 if (nextDoorWind != SK_MaxS32) {
3410 Span& newSpan = fTs[tIndex];
3411 newSpan.fWindValue = nextDoorWind;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003412 newSpan.fOppValue = nextOppWind;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003413 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003414 newSpan.fDone = true;
3415 ++fDoneSpans;
3416 }
3417 }
3418 }
3419
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003420 bool moreHorizontal(int index, int endIndex, bool& unsortable) const {
3421 // find bounds
3422 Bounds bounds;
3423 bounds.setPoint(xyAtT(index));
3424 bounds.add(xyAtT(endIndex));
3425 SkScalar width = bounds.width();
3426 SkScalar height = bounds.height();
3427 if (width > height) {
3428 if (approximately_negative(width)) {
3429 unsortable = true; // edge is too small to resolve meaningfully
3430 }
3431 return false;
3432 } else {
3433 if (approximately_negative(height)) {
3434 unsortable = true; // edge is too small to resolve meaningfully
3435 }
3436 return true;
3437 }
3438 }
3439
caryclark@google.com9764cc62012-07-12 19:29:45 +00003440 // return span if when chasing, two or more radiating spans are not done
3441 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
3442 // candidate and the remaining spans have windValue == 0 (canceled by
3443 // coincidence). The coincident edges could either be removed altogether,
3444 // or this code could be more complicated in detecting this case. Worth it?
3445 bool multipleSpans(int end) const {
3446 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003447 }
3448
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003449 bool nextCandidate(int& start, int& end) const {
3450 do {
3451 start = end;
3452 if (fTs[start].fT == 1) {
3453 return false;
3454 }
3455 end = nextExactSpan(start, 1);
3456 } while (fTs[start].fDone);
3457 return true;
3458 }
3459
caryclark@google.com4eeda372012-12-06 21:47:48 +00003460 Segment* nextChase(int& index, const int step, int& min, Span*& last) const {
3461 int end = nextExactSpan(index, step);
3462 SkASSERT(end >= 0);
3463 if (multipleSpans(end)) {
3464 last = &fTs[end];
3465 return NULL;
3466 }
3467 const Span& endSpan = fTs[end];
3468 Segment* other = endSpan.fOther;
3469 index = endSpan.fOtherIndex;
3470 int otherEnd = other->nextExactSpan(index, step);
3471 min = SkMin32(index, otherEnd);
3472 return other;
3473 }
3474
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003475 // This has callers for two different situations: one establishes the end
3476 // of the current span, and one establishes the beginning of the next span
3477 // (thus the name). When this is looking for the end of the current span,
3478 // coincidence is found when the beginning Ts contain -step and the end
3479 // contains step. When it is looking for the beginning of the next, the
3480 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003481 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003482 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003483 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00003484 int count = fTs.count();
3485 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00003486 while (step > 0 ? ++to < count : --to >= 0) {
3487 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003488 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003489 continue;
3490 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003491 return to;
3492 }
3493 return -1;
3494 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00003495
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003496 // FIXME
3497 // this returns at any difference in T, vs. a preset minimum. It may be
3498 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003499 // OPTIMIZATION splitting this into separate loops for up/down steps
3500 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003501 int nextExactSpan(int from, int step) const {
3502 const Span& fromSpan = fTs[from];
3503 int count = fTs.count();
3504 int to = from;
3505 while (step > 0 ? ++to < count : --to >= 0) {
3506 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00003507 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003508 continue;
3509 }
3510 return to;
3511 }
3512 return -1;
3513 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003514
caryclark@google.com235f56a2012-09-14 14:19:30 +00003515 bool operand() const {
3516 return fOperand;
3517 }
3518
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003519 int oppSign(const Angle* angle) const {
3520 SkASSERT(angle->segment() == this);
3521 return oppSign(angle->start(), angle->end());
3522 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003523
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003524 int oppSign(int startIndex, int endIndex) const {
3525 int result = startIndex < endIndex ? -fTs[startIndex].fOppValue
3526 : fTs[endIndex].fOppValue;
3527#if DEBUG_WIND_BUMP
3528 SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
3529#endif
3530 return result;
3531 }
3532
caryclark@google.com31143cf2012-11-09 22:14:19 +00003533 int oppSum(int tIndex) const {
3534 return fTs[tIndex].fOppSum;
3535 }
3536
3537 int oppSum(const Angle* angle) const {
3538 int lesser = SkMin32(angle->start(), angle->end());
3539 return fTs[lesser].fOppSum;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003540 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003541
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003542 int oppValue(int tIndex) const {
3543 return fTs[tIndex].fOppValue;
3544 }
3545
caryclark@google.come7bd5f42012-12-13 19:47:53 +00003546 int oppValue(const Angle* angle) const {
3547 int lesser = SkMin32(angle->start(), angle->end());
3548 return fTs[lesser].fOppValue;
3549 }
3550
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003551 const SkPoint* pts() const {
3552 return fPts;
3553 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003554
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003555 void reset() {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003556 init(NULL, (SkPath::Verb) -1, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003557 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
3558 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003559 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00003560
caryclark@google.com4eeda372012-12-06 21:47:48 +00003561 void setOppXor(bool isOppXor) {
3562 fOppXor = isOppXor;
3563 }
3564
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003565 void setUpWinding(int index, int endIndex, int& maxWinding, int& sumWinding) {
3566 int deltaSum = spanSign(index, endIndex);
3567 maxWinding = sumWinding;
3568 sumWinding = sumWinding -= deltaSum;
3569 }
3570
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003571 void setUpWindings(int index, int endIndex, int& sumMiWinding, int& sumSuWinding,
3572 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
3573 int deltaSum = spanSign(index, endIndex);
3574 int oppDeltaSum = oppSign(index, endIndex);
3575 if (operand()) {
3576 maxWinding = sumSuWinding;
3577 sumWinding = sumSuWinding -= deltaSum;
3578 oppMaxWinding = sumMiWinding;
3579 oppSumWinding = sumMiWinding -= oppDeltaSum;
3580 } else {
3581 maxWinding = sumMiWinding;
3582 sumWinding = sumMiWinding -= deltaSum;
3583 oppMaxWinding = sumSuWinding;
3584 oppSumWinding = sumSuWinding -= oppDeltaSum;
3585 }
3586 }
3587
caryclark@google.comf839c032012-10-26 21:03:50 +00003588 // This marks all spans unsortable so that this info is available for early
3589 // exclusion in find top and others. This could be optimized to only mark
3590 // adjacent spans that unsortable. However, this makes it difficult to later
3591 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003592 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003593 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003594 int angleCount = angles.count();
3595 int angleIndex;
3596 angleList.setReserve(angleCount);
3597 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003598 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00003599 *angleList.append() = &angle;
3600 sortable &= !angle.unsortable();
3601 }
3602 if (sortable) {
3603 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3604 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3605 if (angles[angleIndex].unsortable()) {
3606 sortable = false;
3607 break;
3608 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003609 }
3610 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003611 if (!sortable) {
3612 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3613 Angle& angle = angles[angleIndex];
3614 angle.segment()->markUnsortable(angle.start(), angle.end());
3615 }
3616 }
3617 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003618 }
3619
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003620 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003621 const Span& span(int tIndex) const {
3622 return fTs[tIndex];
3623 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003624
caryclark@google.com235f56a2012-09-14 14:19:30 +00003625 int spanSign(const Angle* angle) const {
3626 SkASSERT(angle->segment() == this);
3627 return spanSign(angle->start(), angle->end());
3628 }
3629
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003630 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003631 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue
3632 : fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003633#if DEBUG_WIND_BUMP
3634 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3635#endif
3636 return result;
3637 }
3638
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003639 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003640 double t(int tIndex) const {
3641 return fTs[tIndex].fT;
3642 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003643
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003644 bool tiny(const Angle* angle) const {
3645 int start = angle->start();
3646 int end = angle->end();
caryclark@google.comf839c032012-10-26 21:03:50 +00003647 const Span& mSpan = fTs[SkMin32(start, end)];
3648 return mSpan.fTiny;
3649 }
3650
caryclark@google.com18063442012-07-25 12:05:18 +00003651 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3652 double start) {
3653 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003654 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003655 *outsideTs.append() = end;
3656 *outsideTs.append() = start;
3657 }
3658 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00003659
caryclark@google.com24bec792012-08-20 12:43:57 +00003660 void undoneSpan(int& start, int& end) {
3661 size_t tCount = fTs.count();
3662 size_t index;
3663 for (index = 0; index < tCount; ++index) {
3664 if (!fTs[index].fDone) {
3665 break;
3666 }
3667 }
3668 SkASSERT(index < tCount - 1);
3669 start = index;
3670 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003671 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003672 SkASSERT(index < tCount);
3673 SkASSERT(index < tCount);
3674 end = index;
3675 }
caryclark@google.com18063442012-07-25 12:05:18 +00003676
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003677 bool unsortable(int index) const {
3678 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3679 }
3680
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003681 void updatePts(const SkPoint pts[]) {
3682 fPts = pts;
3683 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003684
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003685 int updateOppWinding(int index, int endIndex) const {
3686 int lesser = SkMin32(index, endIndex);
3687 int oppWinding = oppSum(lesser);
3688 int oppSpanWinding = oppSign(index, endIndex);
3689 if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)) {
3690 oppWinding -= oppSpanWinding;
3691 }
3692 return oppWinding;
3693 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00003694
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003695 int updateOppWinding(const Angle* angle) const {
3696 int startIndex = angle->start();
3697 int endIndex = angle->end();
3698 return updateOppWinding(endIndex, startIndex);
3699 }
3700
3701 int updateOppWindingReverse(const Angle* angle) const {
3702 int startIndex = angle->start();
3703 int endIndex = angle->end();
3704 return updateOppWinding(startIndex, endIndex);
3705 }
3706
3707 int updateWinding(int index, int endIndex) const {
3708 int lesser = SkMin32(index, endIndex);
3709 int winding = windSum(lesser);
3710 int spanWinding = spanSign(index, endIndex);
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00003711 if (winding && useInnerWinding(winding - spanWinding, winding)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003712 winding -= spanWinding;
3713 }
3714 return winding;
3715 }
3716
3717 int updateWinding(const Angle* angle) const {
3718 int startIndex = angle->start();
3719 int endIndex = angle->end();
3720 return updateWinding(endIndex, startIndex);
3721 }
3722
3723 int updateWindingReverse(const Angle* angle) const {
3724 int startIndex = angle->start();
3725 int endIndex = angle->end();
3726 return updateWinding(startIndex, endIndex);
3727 }
3728
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003729 SkPath::Verb verb() const {
3730 return fVerb;
3731 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003732
3733 int windingAtTX(double tHit, int tIndex, bool crossOpp) const {
3734 if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
3735 return SK_MinS32;
3736 }
3737 int endIndex = nextSpan(tIndex, 1);
3738 if (crossOpp) {
3739 return updateOppWinding(tIndex, endIndex);
3740 }
3741 return updateWinding(tIndex, endIndex);
3742 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003743
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003744 int windingAtT(double tHit, int tIndex, bool crossOpp, bool& zeroDx) const {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003745 if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
3746 return SK_MinS32;
3747 }
3748 int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
3749 SkASSERT(winding != SK_MinS32);
3750 int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
3751 #if DEBUG_WINDING
3752 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
3753 windVal);
3754 #endif
3755 // see if a + change in T results in a +/- change in X (compute x'(T))
3756 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, tHit);
3757 if (fVerb > SkPath::kLine_Verb && approximately_zero(dx)) {
3758 dx = fPts[2].fX - fPts[1].fX - dx;
3759 }
3760 #if DEBUG_WINDING
3761 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
3762 #endif
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003763 if (dx == 0) {
3764 zeroDx = true;
3765 return SK_MinS32;
3766 }
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003767 if (winding * dx > 0) { // if same signs, result is negative
3768 winding += dx > 0 ? -windVal : windVal;
3769 #if DEBUG_WINDING
3770 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
3771 #endif
3772 }
3773 return winding;
3774 }
3775
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003776 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003777 return fTs[tIndex].fWindSum;
3778 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003779
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003780 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003781 int start = angle->start();
3782 int end = angle->end();
3783 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003784 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003785 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003786
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003787 int windValue(int tIndex) const {
3788 return fTs[tIndex].fWindValue;
3789 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003790
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003791 int windValue(const Angle* angle) const {
3792 int start = angle->start();
3793 int end = angle->end();
3794 int index = SkMin32(start, end);
3795 return windValue(index);
3796 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003797
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003798 SkScalar xAtT(const Span* span) const {
3799 return xyAtT(span).fX;
3800 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003801
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003802 const SkPoint& xyAtT(int index) const {
3803 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003804 }
3805
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003806 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003807 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003808 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003809 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003810 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003811 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003812 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003813 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003814 }
3815 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003816 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003817 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003818
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003819 // used only by right angle winding finding
3820 void xyAtT(int start, int end, double mid, SkPoint& pt) const {
3821 double t = fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
3822 (*SegmentXYAtT[fVerb])(fPts, t, &pt);
3823 }
3824
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003825 SkScalar yAtT(int index) const {
3826 return yAtT(&fTs[index]);
3827 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003828
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003829 SkScalar yAtT(const Span* span) const {
3830 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003831 }
3832
caryclark@google.com4eeda372012-12-06 21:47:48 +00003833 void zeroCoincidentOpp(Span* oTest, int index) {
3834 Span* const test = &fTs[index];
3835 Span* end = test;
3836 do {
3837 end->fOppValue = 0;
3838 end = &fTs[++index];
3839 } while (approximately_negative(end->fT - test->fT));
3840 }
3841
3842 void zeroCoincidentOther(Span* test, const double tRatio, const double oEndT, int oIndex) {
3843 Span* const oTest = &fTs[oIndex];
3844 Span* oEnd = oTest;
3845 const double startT = test->fT;
3846 const double oStartT = oTest->fT;
3847 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
3848 while (!approximately_negative(oEndT - oEnd->fT)
3849 && approximately_negative(oEnd->fT - otherTMatch)) {
3850 oEnd->fOppValue = 0;
3851 oEnd = &fTs[++oIndex];
3852 }
3853 }
3854
3855 void zeroSpan(Span* span) {
3856 SkASSERT(span->fWindValue > 0 || span->fOppValue > 0);
caryclark@google.com6ec15262012-11-16 20:16:50 +00003857 span->fWindValue = 0;
caryclark@google.com729e1c42012-11-21 21:36:34 +00003858 span->fOppValue = 0;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003859 SkASSERT(!span->fDone);
3860 span->fDone = true;
3861 ++fDoneSpans;
caryclark@google.com6ec15262012-11-16 20:16:50 +00003862 }
3863
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003864#if DEBUG_DUMP
3865 void dump() const {
3866 const char className[] = "Segment";
3867 const int tab = 4;
3868 for (int i = 0; i < fTs.count(); ++i) {
3869 SkPoint out;
3870 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3871 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003872 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003873 tab + sizeof(className), className, fID,
3874 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003875 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003876 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003877 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003878 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003879 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003880 }
3881#endif
3882
caryclark@google.com47580692012-07-23 12:14:49 +00003883#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003884 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003885 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003886 for (int i = 0; i < fTs.count(); ++i) {
3887 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3888 return;
3889 }
3890 }
3891 SkASSERT(0);
3892 }
3893#endif
3894
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003895#if DEBUG_DUMP
3896 int debugID() const {
3897 return fID;
3898 }
3899#endif
3900
caryclark@google.com24bec792012-08-20 12:43:57 +00003901#if DEBUG_WINDING
3902 void debugShowSums() const {
3903 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3904 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3905 for (int i = 0; i < fTs.count(); ++i) {
3906 const Span& span = fTs[i];
3907 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3908 if (span.fWindSum == SK_MinS32) {
3909 SkDebugf("?");
3910 } else {
3911 SkDebugf("%d", span.fWindSum);
3912 }
3913 SkDebugf("]");
3914 }
3915 SkDebugf("\n");
3916 }
3917#endif
3918
caryclark@google.comcc905052012-07-25 20:59:42 +00003919#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003920 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003921 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003922 int lastWind = -1;
3923 int lastOpp = -1;
3924 double lastT = -1;
3925 int i;
3926 for (i = 0; i < fTs.count(); ++i) {
3927 bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
3928 || lastOpp != fTs[i].fOppValue;
3929 if (change && lastWind >= 0) {
3930 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3931 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3932 }
3933 if (change) {
3934 SkDebugf(" [o=%d", fTs[i].fOther->fID);
3935 lastWind = fTs[i].fWindValue;
3936 lastOpp = fTs[i].fOppValue;
3937 lastT = fTs[i].fT;
3938 } else {
3939 SkDebugf(",%d", fTs[i].fOther->fID);
3940 }
3941 }
3942 if (i <= 0) {
3943 return;
3944 }
3945 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3946 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3947 if (fOperand) {
3948 SkDebugf(" operand");
3949 }
3950 if (done()) {
3951 SkDebugf(" done");
caryclark@google.com47580692012-07-23 12:14:49 +00003952 }
3953 SkDebugf("\n");
3954 }
3955#endif
3956
caryclark@google.com027de222012-07-12 12:52:50 +00003957#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003958 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003959 if (done()) {
3960 return;
3961 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003962#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3963 int lastId = -1;
3964 double lastT = -1;
3965#endif
caryclark@google.com027de222012-07-12 12:52:50 +00003966 for (int i = 0; i < fTs.count(); ++i) {
3967 if (fTs[i].fDone) {
3968 continue;
3969 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003970#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3971 if (lastId == fID && lastT == fTs[i].fT) {
3972 continue;
3973 }
3974 lastId = fID;
3975 lastT = fTs[i].fT;
3976#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003977 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003978 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3979 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3980 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3981 }
3982 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003983 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003984 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003985 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003986 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3987 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3988 if (fTs[i].fWindSum == SK_MinS32) {
3989 SkDebugf("?");
3990 } else {
3991 SkDebugf("%d", fTs[i].fWindSum);
3992 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003993 SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003994 }
3995 }
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +00003996
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003997 // This isn't useful yet -- but leaving it in for now in case i think of something
3998 // to use it for
3999 void validateActiveSpans() const {
4000 if (done()) {
4001 return;
4002 }
4003 int tCount = fTs.count();
4004 for (int index = 0; index < tCount; ++index) {
4005 if (fTs[index].fDone) {
4006 continue;
4007 }
4008 // count number of connections which are not done
4009 int first = index;
4010 double baseT = fTs[index].fT;
4011 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
4012 --first;
4013 }
4014 int last = index;
4015 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
4016 ++last;
4017 }
4018 int connections = 0;
4019 connections += first > 0 && !fTs[first - 1].fDone;
4020 for (int test = first; test <= last; ++test) {
4021 connections += !fTs[test].fDone;
4022 const Segment* other = fTs[test].fOther;
4023 int oIndex = fTs[test].fOtherIndex;
4024 connections += !other->fTs[oIndex].fDone;
4025 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
4026 }
4027 // SkASSERT(!(connections & 1));
4028 }
4029 }
caryclark@google.com027de222012-07-12 12:52:50 +00004030#endif
4031
caryclark@google.com0c803d02012-08-06 11:15:47 +00004032#if DEBUG_MARK_DONE
4033 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
4034 const SkPoint& pt = xyAtT(&span);
4035 SkDebugf("%s id=%d", fun, fID);
4036 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
4037 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
4038 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
4039 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004040 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
4041 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
4042 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
4043 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00004044 if (span.fWindSum == SK_MinS32) {
4045 SkDebugf("?");
4046 } else {
4047 SkDebugf("%d", span.fWindSum);
4048 }
4049 SkDebugf(" windValue=%d\n", span.fWindValue);
4050 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004051
4052 void debugShowNewWinding(const char* fun, const Span& span, int winding, int oppWinding) {
4053 const SkPoint& pt = xyAtT(&span);
4054 SkDebugf("%s id=%d", fun, fID);
4055 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
4056 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
4057 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
4058 }
4059 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
4060 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
4061 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d newOppSum=%d oppSum=",
4062 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
4063 winding, oppWinding);
4064 if (span.fOppSum == SK_MinS32) {
4065 SkDebugf("?");
4066 } else {
4067 SkDebugf("%d", span.fOppSum);
4068 }
4069 SkDebugf(" windSum=");
4070 if (span.fWindSum == SK_MinS32) {
4071 SkDebugf("?");
4072 } else {
4073 SkDebugf("%d", span.fWindSum);
4074 }
4075 SkDebugf(" windValue=%d\n", span.fWindValue);
4076 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00004077#endif
4078
caryclark@google.com47580692012-07-23 12:14:49 +00004079#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00004080 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com31143cf2012-11-09 22:14:19 +00004081 const int contourWinding, const int oppContourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00004082 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00004083 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004084 int lastSum = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004085 int oppLastSum = oppContourWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004086 const Angle* firstAngle = angles[first];
4087 int windSum = lastSum - spanSign(firstAngle);
4088 int oppoSign = oppSign(firstAngle);
4089 int oppWindSum = oppLastSum - oppoSign;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004090 SkDebugf("%s %s contourWinding=%d oppContourWinding=%d sign=%d\n", fun, __FUNCTION__,
4091 contourWinding, oppContourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00004092 int index = first;
4093 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00004094 do {
4095 const Angle& angle = *angles[index];
4096 const Segment& segment = *angle.segment();
4097 int start = angle.start();
4098 int end = angle.end();
4099 const Span& sSpan = segment.fTs[start];
4100 const Span& eSpan = segment.fTs[end];
4101 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com31143cf2012-11-09 22:14:19 +00004102 bool opp = segment.fOperand ^ fOperand;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004103 if (!firstTime) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004104 oppoSign = segment.oppSign(&angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004105 if (opp) {
4106 oppLastSum = oppWindSum;
4107 oppWindSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004108 if (oppoSign) {
4109 lastSum = windSum;
4110 windSum -= oppoSign;
4111 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004112 } else {
4113 lastSum = windSum;
4114 windSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004115 if (oppoSign) {
4116 oppLastSum = oppWindSum;
4117 oppWindSum -= oppoSign;
4118 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004119 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00004120 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004121 SkDebugf("%s [%d] %sid=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00004122 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004123 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004124 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004125 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
4126 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
4127 mSpan.fWindValue);
4128 if (mSpan.fWindSum == SK_MinS32) {
4129 SkDebugf("?");
4130 } else {
4131 SkDebugf("%d", mSpan.fWindSum);
4132 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004133 int last, wind;
4134 if (opp) {
4135 last = oppLastSum;
4136 wind = oppWindSum;
4137 } else {
4138 last = lastSum;
4139 wind = windSum;
4140 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004141 if (!oppoSign) {
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00004142 SkDebugf(" %d->%d (max=%d)", last, wind,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004143 useInnerWinding(last, wind) ? wind : last);
4144 } else {
4145 SkDebugf(" %d->%d (%d->%d)", last, wind, opp ? lastSum : oppLastSum,
4146 opp ? windSum : oppWindSum);
4147 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004148 SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004149#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00004150 angle.debugShow(segment.xyAtT(&sSpan));
4151#endif
caryclark@google.com47580692012-07-23 12:14:49 +00004152 ++index;
4153 if (index == angles.count()) {
4154 index = 0;
4155 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004156 if (firstTime) {
4157 firstTime = false;
4158 }
caryclark@google.com47580692012-07-23 12:14:49 +00004159 } while (index != first);
4160 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00004161
4162 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first) {
4163 const Angle* firstAngle = angles[first];
4164 const Segment* segment = firstAngle->segment();
4165 int winding = segment->updateWinding(firstAngle);
4166 int oppWinding = segment->updateOppWinding(firstAngle);
4167 debugShowSort(fun, angles, first, winding, oppWinding);
4168 }
4169
caryclark@google.com47580692012-07-23 12:14:49 +00004170#endif
4171
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004172#if DEBUG_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004173 static char as_digit(int value) {
4174 return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
4175 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00004176#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004177
caryclark@google.com729e1c42012-11-21 21:36:34 +00004178#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004179 int debugShowWindingValues(int slotCount, int ofInterest) const {
4180 if (!(1 << fID & ofInterest)) {
4181 return 0;
4182 }
4183 int sum = 0;
4184 SkTDArray<char> slots;
4185 slots.setCount(slotCount * 2);
4186 memset(slots.begin(), ' ', slotCount * 2);
4187 for (int i = 0; i < fTs.count(); ++i) {
4188 // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
4189 // continue;
4190 // }
4191 sum += fTs[i].fWindValue;
4192 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
4193 sum += fTs[i].fOppValue;
4194 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
4195 }
4196 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
4197 slots.begin() + slotCount);
4198 return sum;
4199 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00004200#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004201
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004202private:
4203 const SkPoint* fPts;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004204 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004205 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com4eeda372012-12-06 21:47:48 +00004206 // OPTIMIZATION: could pack donespans, verb, operand, xor into 1 int-sized value
caryclark@google.com24bec792012-08-20 12:43:57 +00004207 int fDoneSpans; // quick check that segment is finished
caryclark@google.com4eeda372012-12-06 21:47:48 +00004208 // OPTIMIZATION: force the following to be byte-sized
4209 SkPath::Verb fVerb;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004210 bool fOperand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00004211 bool fXor; // set if original contour had even-odd fill
4212 bool fOppXor; // set if opposite operand had even-odd fill
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004213#if DEBUG_DUMP
4214 int fID;
4215#endif
4216};
4217
caryclark@google.comb9738012012-07-03 19:53:30 +00004218class Contour;
4219
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004220struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00004221 Contour* fContours[2];
4222 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004223 double fTs[2][2];
4224};
4225
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004226class Contour {
4227public:
4228 Contour() {
4229 reset();
4230#if DEBUG_DUMP
4231 fID = ++gContourID;
4232#endif
4233 }
4234
4235 bool operator<(const Contour& rh) const {
4236 return fBounds.fTop == rh.fBounds.fTop
4237 ? fBounds.fLeft < rh.fBounds.fLeft
4238 : fBounds.fTop < rh.fBounds.fTop;
4239 }
4240
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004241 void addCoincident(int index, Contour* other, int otherIndex,
4242 const Intersections& ts, bool swap) {
4243 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004244 coincidence.fContours[0] = this; // FIXME: no need to store
caryclark@google.comb9738012012-07-03 19:53:30 +00004245 coincidence.fContours[1] = other;
4246 coincidence.fSegments[0] = index;
4247 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00004248 if (fSegments[index].verb() == SkPath::kLine_Verb &&
4249 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
4250 // FIXME: coincident lines use legacy Ts instead of coincident Ts
4251 coincidence.fTs[swap][0] = ts.fT[0][0];
4252 coincidence.fTs[swap][1] = ts.fT[0][1];
4253 coincidence.fTs[!swap][0] = ts.fT[1][0];
4254 coincidence.fTs[!swap][1] = ts.fT[1][1];
4255 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
4256 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
4257 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
4258 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
4259 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
4260 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
4261 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004262 }
4263
4264 void addCross(const Contour* crosser) {
4265#ifdef DEBUG_CROSS
4266 for (int index = 0; index < fCrosses.count(); ++index) {
4267 SkASSERT(fCrosses[index] != crosser);
4268 }
4269#endif
4270 *fCrosses.append() = crosser;
4271 }
4272
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004273 void addCubic(const SkPoint pts[4]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004274 fSegments.push_back().addCubic(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004275 fContainsCurves = true;
4276 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004277
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004278 int addLine(const SkPoint pts[2]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004279 fSegments.push_back().addLine(pts, fOperand, fXor);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004280 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004281 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004282
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004283 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
4284 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
4285 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004286
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004287 int addQuad(const SkPoint pts[3]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004288 fSegments.push_back().addQuad(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004289 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004290 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004291 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004292
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004293 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
4294 containsIntercepts();
4295 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
4296 }
4297
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004298 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004299 return fBounds;
4300 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004301
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004302 void complete() {
4303 setBounds();
4304 fContainsIntercepts = false;
4305 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004306
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004307 void containsIntercepts() {
4308 fContainsIntercepts = true;
4309 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004310
caryclark@google.come7bd5f42012-12-13 19:47:53 +00004311 const Segment* crossedSegmentY(const SkPoint& basePt, SkScalar& bestY,
4312 int &tIndex, double& hitT, bool opp) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004313 int segmentCount = fSegments.count();
4314 const Segment* bestSegment = NULL;
4315 for (int test = 0; test < segmentCount; ++test) {
4316 Segment* testSegment = &fSegments[test];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004317 double testHitT;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00004318 int testT = testSegment->crossedSpanY(basePt, bestY, testHitT, opp);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004319 if (testT >= 0) {
4320 bestSegment = testSegment;
4321 tIndex = testT;
4322 hitT = testHitT;
4323 }
4324 }
4325 return bestSegment;
4326 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004327
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004328 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004329 for (int index = 0; index < fCrosses.count(); ++index) {
4330 if (fCrosses[index] == crosser) {
4331 return true;
4332 }
4333 }
4334 return false;
4335 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00004336
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004337 bool done() const {
4338 return fDone;
4339 }
4340
caryclark@google.comf839c032012-10-26 21:03:50 +00004341 const SkPoint& end() const {
4342 const Segment& segment = fSegments.back();
4343 return segment.pts()[segment.verb()];
4344 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004345
caryclark@google.com4eeda372012-12-06 21:47:48 +00004346 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004347 int segmentCount = fSegments.count();
4348 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004349 fSegments[sIndex].findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004350 }
4351 }
4352
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004353 void fixOtherTIndex() {
4354 int segmentCount = fSegments.count();
4355 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
4356 fSegments[sIndex].fixOtherTIndex();
4357 }
4358 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00004359
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004360 Segment* nonVerticalSegment(int& start, int& end) {
4361 int segmentCount = fSortedSegments.count();
4362 SkASSERT(segmentCount > 0);
4363 for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
4364 Segment* testSegment = fSortedSegments[sortedIndex];
4365 if (testSegment->done()) {
4366 continue;
4367 }
4368 start = end = 0;
4369 while (testSegment->nextCandidate(start, end)) {
4370 if (!testSegment->isVertical(start, end)) {
4371 return testSegment;
4372 }
4373 }
4374 }
4375 return NULL;
4376 }
4377
caryclark@google.com31143cf2012-11-09 22:14:19 +00004378 bool operand() const {
4379 return fOperand;
4380 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004381
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004382 void reset() {
4383 fSegments.reset();
4384 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004385 fContainsCurves = fContainsIntercepts = fDone = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004386 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004387
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004388 void resolveCoincidence(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004389 int count = fCoincidences.count();
4390 for (int index = 0; index < count; ++index) {
4391 Coincidence& coincidence = fCoincidences[index];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004392 SkASSERT(coincidence.fContours[0] == this);
caryclark@google.comb9738012012-07-03 19:53:30 +00004393 int thisIndex = coincidence.fSegments[0];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004394 Segment& thisOne = fSegments[thisIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004395 Contour* otherContour = coincidence.fContours[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00004396 int otherIndex = coincidence.fSegments[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00004397 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004398 if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004399 continue;
4400 }
caryclark@google.com47580692012-07-23 12:14:49 +00004401 #if DEBUG_CONCIDENT
4402 thisOne.debugShowTs();
4403 other.debugShowTs();
4404 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004405 double startT = coincidence.fTs[0][0];
4406 double endT = coincidence.fTs[0][1];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004407 bool cancelers = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004408 if (startT > endT) {
4409 SkTSwap<double>(startT, endT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004410 cancelers ^= true; // FIXME: just assign true
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004411 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004412 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004413 double oStartT = coincidence.fTs[1][0];
4414 double oEndT = coincidence.fTs[1][1];
4415 if (oStartT > oEndT) {
4416 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004417 cancelers ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004418 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004419 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00004420 bool opp = fOperand ^ otherContour->fOperand;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004421 if (cancelers && !opp) {
4422 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00004423 if (startT > 0 || oEndT < 1
4424 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
4425 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004426 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004427 if (oStartT > 0 || endT < 1
4428 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
4429 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004430 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004431 if (!thisOne.done() && !other.done()) {
4432 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
4433 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004434 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004435 if (startT > 0 || oStartT > 0
4436 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00004437 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004438 }
caryclark@google.com200c2112012-08-03 15:05:04 +00004439 if (endT < 1 || oEndT < 1
4440 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00004441 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004442 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004443 if (!thisOne.done() && !other.done()) {
4444 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
4445 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004446 }
caryclark@google.com47580692012-07-23 12:14:49 +00004447 #if DEBUG_CONCIDENT
4448 thisOne.debugShowTs();
4449 other.debugShowTs();
4450 #endif
caryclark@google.com729e1c42012-11-21 21:36:34 +00004451 #if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004452 debugShowWindingValues(contourList);
4453 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004454 }
4455 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004456
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004457 SkTArray<Segment>& segments() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004458 return fSegments;
4459 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004460
caryclark@google.com235f56a2012-09-14 14:19:30 +00004461 void setOperand(bool isOp) {
4462 fOperand = isOp;
4463 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00004464
caryclark@google.com4eeda372012-12-06 21:47:48 +00004465 void setOppXor(bool isOppXor) {
4466 fOppXor = isOppXor;
4467 int segmentCount = fSegments.count();
4468 for (int test = 0; test < segmentCount; ++test) {
4469 fSegments[test].setOppXor(isOppXor);
4470 }
4471 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004472
caryclark@google.com235f56a2012-09-14 14:19:30 +00004473 void setXor(bool isXor) {
4474 fXor = isXor;
4475 }
4476
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004477 void sortSegments() {
4478 int segmentCount = fSegments.count();
4479 fSortedSegments.setReserve(segmentCount);
4480 for (int test = 0; test < segmentCount; ++test) {
4481 *fSortedSegments.append() = &fSegments[test];
4482 }
4483 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
4484 fFirstSorted = 0;
4485 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004486
caryclark@google.comf839c032012-10-26 21:03:50 +00004487 const SkPoint& start() const {
4488 return fSegments.front().pts()[0];
4489 }
4490
4491 void toPath(PathWrapper& path) const {
4492 int segmentCount = fSegments.count();
4493 const SkPoint& pt = fSegments.front().pts()[0];
4494 path.deferredMove(pt);
4495 for (int test = 0; test < segmentCount; ++test) {
4496 fSegments[test].addCurveTo(0, 1, path, true);
4497 }
4498 path.close();
4499 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00004500
caryclark@google.comf839c032012-10-26 21:03:50 +00004501 void toPartialBackward(PathWrapper& path) const {
4502 int segmentCount = fSegments.count();
4503 for (int test = segmentCount - 1; test >= 0; --test) {
4504 fSegments[test].addCurveTo(1, 0, path, true);
4505 }
4506 }
4507
4508 void toPartialForward(PathWrapper& path) const {
4509 int segmentCount = fSegments.count();
4510 for (int test = 0; test < segmentCount; ++test) {
4511 fSegments[test].addCurveTo(0, 1, path, true);
4512 }
4513 }
4514
4515#if 0 // FIXME: obsolete, remove
caryclark@google.com15fa1382012-05-07 20:49:36 +00004516 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
4517 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00004518 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00004519 // segments' top, and not a true sort, so it could be ameniable to regular
4520 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004521 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00004522 int segmentCount = fSegments.count();
4523 SkASSERT(segmentCount > 0);
4524 int best = -1;
4525 Segment* bestSegment = NULL;
4526 while (++best < segmentCount) {
4527 Segment* testSegment = &fSegments[best];
4528 if (testSegment->done()) {
4529 continue;
4530 }
4531 bestSegment = testSegment;
4532 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004533 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004534 if (!bestSegment) {
4535 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004536 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004537 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00004538 for (int test = best + 1; test < segmentCount; ++test) {
4539 Segment* testSegment = &fSegments[test];
4540 if (testSegment->done()) {
4541 continue;
4542 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004543 if (testSegment->bounds().fTop > bestTop) {
4544 continue;
4545 }
4546 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00004547 if (bestTop > testTop) {
4548 bestTop = testTop;
4549 bestSegment = testSegment;
4550 }
4551 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004552 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004553 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004554 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004555#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004556
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004557 void topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY, Segment*& topStart) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004558 int segmentCount = fSortedSegments.count();
4559 SkASSERT(segmentCount > 0);
caryclark@google.comf839c032012-10-26 21:03:50 +00004560 int sortedIndex = fFirstSorted;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004561 fDone = true; // may be cleared below
caryclark@google.comf839c032012-10-26 21:03:50 +00004562 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
4563 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004564 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00004565 if (sortedIndex == fFirstSorted) {
4566 ++fFirstSorted;
4567 }
4568 continue;
4569 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004570 fDone = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00004571 SkPoint testXY;
4572 testSegment->activeLeftTop(testXY);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004573 if (topStart) {
4574 if (testXY.fY < topLeft.fY) {
4575 continue;
4576 }
4577 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
4578 continue;
4579 }
4580 if (bestXY.fY < testXY.fY) {
4581 continue;
4582 }
4583 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
4584 continue;
4585 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004586 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004587 topStart = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00004588 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004589 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004590 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004591
caryclark@google.com24bec792012-08-20 12:43:57 +00004592 Segment* undoneSegment(int& start, int& end) {
4593 int segmentCount = fSegments.count();
4594 for (int test = 0; test < segmentCount; ++test) {
4595 Segment* testSegment = &fSegments[test];
4596 if (testSegment->done()) {
4597 continue;
4598 }
4599 testSegment->undoneSpan(start, end);
4600 return testSegment;
4601 }
4602 return NULL;
4603 }
4604
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004605 int updateSegment(int index, const SkPoint* pts) {
4606 Segment& segment = fSegments[index];
4607 segment.updatePts(pts);
4608 return segment.verb() + 1;
4609 }
4610
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004611#if DEBUG_TEST
4612 SkTArray<Segment>& debugSegments() {
4613 return fSegments;
4614 }
4615#endif
4616
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004617#if DEBUG_DUMP
4618 void dump() {
4619 int i;
4620 const char className[] = "Contour";
4621 const int tab = 4;
4622 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
4623 for (i = 0; i < fSegments.count(); ++i) {
4624 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
4625 className, i);
4626 fSegments[i].dump();
4627 }
4628 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
4629 tab + sizeof(className), className,
4630 fBounds.fLeft, fBounds.fTop,
4631 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004632 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
4633 className, fContainsIntercepts);
4634 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
4635 className, fContainsCurves);
4636 }
4637#endif
4638
caryclark@google.com027de222012-07-12 12:52:50 +00004639#if DEBUG_ACTIVE_SPANS
4640 void debugShowActiveSpans() {
4641 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004642 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00004643 }
4644 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004645
4646 void validateActiveSpans() {
4647 for (int index = 0; index < fSegments.count(); ++index) {
4648 fSegments[index].validateActiveSpans();
4649 }
4650 }
caryclark@google.com027de222012-07-12 12:52:50 +00004651#endif
4652
caryclark@google.com729e1c42012-11-21 21:36:34 +00004653#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004654 int debugShowWindingValues(int totalSegments, int ofInterest) {
4655 int count = fSegments.count();
4656 int sum = 0;
4657 for (int index = 0; index < count; ++index) {
4658 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
4659 }
4660 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
4661 return sum;
4662 }
4663
4664 static void debugShowWindingValues(SkTDArray<Contour*>& contourList) {
4665 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
4666 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
4667 int ofInterest = 1 << 5 | 1 << 8;
4668 int total = 0;
4669 int index;
4670 for (index = 0; index < contourList.count(); ++index) {
4671 total += contourList[index]->segments().count();
4672 }
4673 int sum = 0;
4674 for (index = 0; index < contourList.count(); ++index) {
4675 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
4676 }
4677 // SkDebugf("%s total=%d\n", __FUNCTION__, sum);
4678 }
4679#endif
4680
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004681protected:
4682 void setBounds() {
4683 int count = fSegments.count();
4684 if (count == 0) {
4685 SkDebugf("%s empty contour\n", __FUNCTION__);
4686 SkASSERT(0);
4687 // FIXME: delete empty contour?
4688 return;
4689 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004690 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004691 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004692 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004693 }
4694 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004695
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004696private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004697 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004698 SkTDArray<Segment*> fSortedSegments;
4699 int fFirstSorted;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004700 SkTDArray<Coincidence> fCoincidences;
4701 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004702 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004703 bool fContainsIntercepts;
4704 bool fContainsCurves;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004705 bool fDone;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004706 bool fOperand; // true for the second argument to a binary operator
4707 bool fXor;
caryclark@google.com4eeda372012-12-06 21:47:48 +00004708 bool fOppXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004709#if DEBUG_DUMP
4710 int fID;
4711#endif
4712};
4713
4714class EdgeBuilder {
4715public:
4716
caryclark@google.comf839c032012-10-26 21:03:50 +00004717EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
4718 : fPath(path.nativePath())
4719 , fContours(contours)
4720{
4721 init();
4722}
4723
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004724EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00004725 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004726 , fContours(contours)
4727{
caryclark@google.comf839c032012-10-26 21:03:50 +00004728 init();
4729}
4730
4731void init() {
4732 fCurrentContour = NULL;
4733 fOperand = false;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004734 fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004735#if DEBUG_DUMP
4736 gContourID = 0;
4737 gSegmentID = 0;
4738#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00004739 fSecondHalf = preFetch();
4740}
4741
4742void addOperand(const SkPath& path) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004743 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
4744 fPathVerbs.pop();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004745 fPath = &path;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004746 fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004747 preFetch();
4748}
4749
4750void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004751 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004752 complete();
4753 if (fCurrentContour && !fCurrentContour->segments().count()) {
4754 fContours.pop_back();
4755 }
4756 // correct pointers in contours since fReducePts may have moved as it grew
4757 int cIndex = 0;
4758 int extraCount = fExtra.count();
4759 SkASSERT(extraCount == 0 || fExtra[0] == -1);
4760 int eIndex = 0;
4761 int rIndex = 0;
4762 while (++eIndex < extraCount) {
4763 int offset = fExtra[eIndex];
4764 if (offset < 0) {
4765 ++cIndex;
4766 continue;
4767 }
4768 fCurrentContour = &fContours[cIndex];
4769 rIndex += fCurrentContour->updateSegment(offset - 1,
4770 &fReducePts[rIndex]);
4771 }
4772 fExtra.reset(); // we're done with this
4773}
4774
4775ShapeOpMask xorMask() const {
caryclark@google.com729e1c42012-11-21 21:36:34 +00004776 return fXorMask[fOperand];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004777}
4778
4779protected:
4780
4781void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004782 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004783 fCurrentContour->complete();
4784 fCurrentContour = NULL;
4785 }
4786}
4787
caryclark@google.com235f56a2012-09-14 14:19:30 +00004788// FIXME:remove once we can access path pts directly
4789int preFetch() {
4790 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004791 SkPoint pts[4];
4792 SkPath::Verb verb;
4793 do {
4794 verb = iter.next(pts);
4795 *fPathVerbs.append() = verb;
4796 if (verb == SkPath::kMove_Verb) {
4797 *fPathPts.append() = pts[0];
4798 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
4799 fPathPts.append(verb, &pts[1]);
4800 }
4801 } while (verb != SkPath::kDone_Verb);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004802 return fPathVerbs.count() - 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004803}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004804
caryclark@google.com235f56a2012-09-14 14:19:30 +00004805void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004806 SkPath::Verb reducedVerb;
4807 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004808 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004809 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004810 const SkPoint* finalCurveStart = NULL;
4811 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004812 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004813 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
4814 switch (verb) {
4815 case SkPath::kMove_Verb:
4816 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004817 if (!fCurrentContour) {
4818 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004819 fCurrentContour->setOperand(fOperand);
caryclark@google.com729e1c42012-11-21 21:36:34 +00004820 fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004821 *fExtra.append() = -1; // start new contour
4822 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004823 finalCurveEnd = pointsPtr++;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004824 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004825 case SkPath::kLine_Verb:
4826 // skip degenerate points
4827 if (pointsPtr[-1].fX != pointsPtr[0].fX
4828 || pointsPtr[-1].fY != pointsPtr[0].fY) {
4829 fCurrentContour->addLine(&pointsPtr[-1]);
4830 }
4831 break;
4832 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00004833
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004834 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
4835 if (reducedVerb == 0) {
4836 break; // skip degenerate points
4837 }
4838 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00004839 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004840 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004841 break;
4842 }
4843 fCurrentContour->addQuad(&pointsPtr[-1]);
4844 break;
4845 case SkPath::kCubic_Verb:
4846 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
4847 if (reducedVerb == 0) {
4848 break; // skip degenerate points
4849 }
4850 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004851 *fExtra.append() =
4852 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004853 break;
4854 }
4855 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004856 *fExtra.append() =
4857 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004858 break;
4859 }
4860 fCurrentContour->addCubic(&pointsPtr[-1]);
4861 break;
4862 case SkPath::kClose_Verb:
4863 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004864 if (finalCurveStart && finalCurveEnd
4865 && *finalCurveStart != *finalCurveEnd) {
4866 *fReducePts.append() = *finalCurveStart;
4867 *fReducePts.append() = *finalCurveEnd;
4868 *fExtra.append() =
4869 fCurrentContour->addLine(fReducePts.end() - 2);
4870 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004871 complete();
caryclark@google.com31143cf2012-11-09 22:14:19 +00004872 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004873 default:
4874 SkDEBUGFAIL("bad verb");
4875 return;
4876 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004877 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004878 pointsPtr += verb;
4879 SkASSERT(fCurrentContour);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004880 nextVerb:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004881 if (verbPtr == endOfFirstHalf) {
4882 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004883 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004884 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004885}
4886
4887private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004888 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004889 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004890 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004891 Contour* fCurrentContour;
4892 SkTArray<Contour>& fContours;
4893 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004894 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com729e1c42012-11-21 21:36:34 +00004895 ShapeOpMask fXorMask[2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004896 int fSecondHalf;
4897 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004898};
4899
4900class Work {
4901public:
4902 enum SegmentType {
4903 kHorizontalLine_Segment = -1,
4904 kVerticalLine_Segment = 0,
4905 kLine_Segment = SkPath::kLine_Verb,
4906 kQuad_Segment = SkPath::kQuad_Verb,
4907 kCubic_Segment = SkPath::kCubic_Verb,
4908 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004909
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004910 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4911 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4912 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004913
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004914 // FIXME: does it make sense to write otherIndex now if we're going to
4915 // fix it up later?
4916 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004917 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004918 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004919
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004920 // Avoid collapsing t values that are close to the same since
4921 // we walk ts to describe consecutive intersections. Since a pair of ts can
4922 // be nearly equal, any problems caused by this should be taken care
4923 // of later.
4924 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004925 int addT(double newT, const Work& other) {
4926 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004927 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004928
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004929 bool advance() {
4930 return ++fIndex < fLast;
4931 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004932
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004933 SkScalar bottom() const {
4934 return bounds().fBottom;
4935 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004936
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004937 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004938 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004939 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004940
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004941 const SkPoint* cubic() const {
4942 return fCubic;
4943 }
4944
4945 void init(Contour* contour) {
4946 fContour = contour;
4947 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004948 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004949 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004950
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004951 bool isAdjacent(const Work& next) {
4952 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4953 }
4954
4955 bool isFirstLast(const Work& next) {
4956 return fContour == next.fContour && fIndex == 0
4957 && next.fIndex == fLast - 1;
4958 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004959
4960 SkScalar left() const {
4961 return bounds().fLeft;
4962 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004963
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004964 void promoteToCubic() {
4965 fCubic[0] = pts()[0];
4966 fCubic[2] = pts()[1];
4967 fCubic[3] = pts()[2];
4968 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4969 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4970 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4971 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4972 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004973
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004974 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004975 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004976 }
4977
4978 SkScalar right() const {
4979 return bounds().fRight;
4980 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004981
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004982 ptrdiff_t segmentIndex() const {
4983 return fIndex;
4984 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004985
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004986 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004987 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004988 SegmentType type = (SegmentType) segment.verb();
4989 if (type != kLine_Segment) {
4990 return type;
4991 }
4992 if (segment.isHorizontal()) {
4993 return kHorizontalLine_Segment;
4994 }
4995 if (segment.isVertical()) {
4996 return kVerticalLine_Segment;
4997 }
4998 return kLine_Segment;
4999 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005000
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005001 bool startAfter(const Work& after) {
5002 fIndex = after.fIndex;
5003 return advance();
5004 }
5005
5006 SkScalar top() const {
5007 return bounds().fTop;
5008 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005009
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005010 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005011 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005012 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005013
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005014 SkScalar x() const {
5015 return bounds().fLeft;
5016 }
5017
5018 bool xFlipped() const {
5019 return x() != pts()[0].fX;
5020 }
5021
5022 SkScalar y() const {
5023 return bounds().fTop;
5024 }
5025
5026 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005027 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005028 }
5029
5030protected:
5031 Contour* fContour;
5032 SkPoint fCubic[4];
5033 int fIndex;
5034 int fLast;
5035};
5036
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005037#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005038static void debugShowLineIntersection(int pts, const Work& wt,
5039 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005040 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005041 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005042 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
5043 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
5044 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
5045 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005046 return;
5047 }
5048 SkPoint wtOutPt, wnOutPt;
5049 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
5050 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00005051 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005052 __FUNCTION__,
5053 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
5054 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
5055 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00005056 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005057 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00005058 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005059 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
5060 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
5061 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00005062 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
5063 }
5064 SkDebugf("\n");
5065}
5066
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005067static void debugShowQuadLineIntersection(int pts, const Work& wt,
5068 const Work& wn, const double wtTs[2], const double wnTs[2]) {
5069 if (!pts) {
5070 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00005071 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005072 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
5073 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005074 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005075 return;
5076 }
5077 SkPoint wtOutPt, wnOutPt;
5078 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
5079 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
5080 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5081 __FUNCTION__,
5082 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
5083 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
5084 wtOutPt.fX, wtOutPt.fY);
5085 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005086 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
5087 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005088 }
5089 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5090 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
5091 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
5092 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005093 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
5094 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005095 }
5096 SkDebugf("\n");
5097}
5098
caryclark@google.coma461ff02012-10-11 12:54:23 +00005099static void debugShowQuadIntersection(int pts, const Work& wt,
5100 const Work& wn, const double wtTs[2], const double wnTs[2]) {
5101 if (!pts) {
5102 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
5103 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
5104 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00005105 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00005106 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005107 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00005108 return;
5109 }
5110 SkPoint wtOutPt, wnOutPt;
5111 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
5112 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
5113 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5114 __FUNCTION__,
5115 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
5116 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
5117 wtOutPt.fX, wtOutPt.fY);
5118 if (pts == 2) {
5119 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
5120 }
5121 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5122 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
5123 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
5124 wnOutPt.fX, wnOutPt.fY);
5125 if (pts == 2) {
5126 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005127 }
caryclark@google.comb9738012012-07-03 19:53:30 +00005128 SkDebugf("\n");
5129}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005130#else
5131static void debugShowLineIntersection(int , const Work& ,
5132 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005133}
caryclark@google.coma461ff02012-10-11 12:54:23 +00005134
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005135static void debugShowQuadLineIntersection(int , const Work& ,
5136 const Work& , const double [2], const double [2]) {
5137}
5138
caryclark@google.coma461ff02012-10-11 12:54:23 +00005139static void debugShowQuadIntersection(int , const Work& ,
5140 const Work& , const double [2], const double [2]) {
5141}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005142#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005143
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005144static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005145
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005146 if (test != next) {
5147 if (test->bounds().fBottom < next->bounds().fTop) {
5148 return false;
5149 }
5150 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
5151 return true;
5152 }
5153 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005154 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005155 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005156 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005157 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005158 Work wn;
5159 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005160 if (test == next && !wn.startAfter(wt)) {
5161 continue;
5162 }
5163 do {
5164 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
5165 continue;
5166 }
5167 int pts;
5168 Intersections ts;
5169 bool swap = false;
5170 switch (wt.segmentType()) {
5171 case Work::kHorizontalLine_Segment:
5172 swap = true;
5173 switch (wn.segmentType()) {
5174 case Work::kHorizontalLine_Segment:
5175 case Work::kVerticalLine_Segment:
5176 case Work::kLine_Segment: {
5177 pts = HLineIntersect(wn.pts(), wt.left(),
5178 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005179 debugShowLineIntersection(pts, wt, wn,
5180 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005181 break;
5182 }
5183 case Work::kQuad_Segment: {
5184 pts = HQuadIntersect(wn.pts(), wt.left(),
5185 wt.right(), wt.y(), wt.xFlipped(), ts);
5186 break;
5187 }
5188 case Work::kCubic_Segment: {
5189 pts = HCubicIntersect(wn.pts(), wt.left(),
5190 wt.right(), wt.y(), wt.xFlipped(), ts);
5191 break;
5192 }
5193 default:
5194 SkASSERT(0);
5195 }
5196 break;
5197 case Work::kVerticalLine_Segment:
5198 swap = true;
5199 switch (wn.segmentType()) {
5200 case Work::kHorizontalLine_Segment:
5201 case Work::kVerticalLine_Segment:
5202 case Work::kLine_Segment: {
5203 pts = VLineIntersect(wn.pts(), wt.top(),
5204 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005205 debugShowLineIntersection(pts, wt, wn,
5206 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005207 break;
5208 }
5209 case Work::kQuad_Segment: {
5210 pts = VQuadIntersect(wn.pts(), wt.top(),
5211 wt.bottom(), wt.x(), wt.yFlipped(), ts);
5212 break;
5213 }
5214 case Work::kCubic_Segment: {
5215 pts = VCubicIntersect(wn.pts(), wt.top(),
5216 wt.bottom(), wt.x(), wt.yFlipped(), ts);
5217 break;
5218 }
5219 default:
5220 SkASSERT(0);
5221 }
5222 break;
5223 case Work::kLine_Segment:
5224 switch (wn.segmentType()) {
5225 case Work::kHorizontalLine_Segment:
5226 pts = HLineIntersect(wt.pts(), wn.left(),
5227 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005228 debugShowLineIntersection(pts, wt, wn,
5229 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005230 break;
5231 case Work::kVerticalLine_Segment:
5232 pts = VLineIntersect(wt.pts(), wn.top(),
5233 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005234 debugShowLineIntersection(pts, wt, wn,
5235 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005236 break;
5237 case Work::kLine_Segment: {
5238 pts = LineIntersect(wt.pts(), wn.pts(), ts);
5239 debugShowLineIntersection(pts, wt, wn,
5240 ts.fT[1], ts.fT[0]);
5241 break;
5242 }
5243 case Work::kQuad_Segment: {
5244 swap = true;
5245 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005246 debugShowQuadLineIntersection(pts, wn, wt,
5247 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005248 break;
5249 }
5250 case Work::kCubic_Segment: {
5251 swap = true;
5252 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
5253 break;
5254 }
5255 default:
5256 SkASSERT(0);
5257 }
5258 break;
5259 case Work::kQuad_Segment:
5260 switch (wn.segmentType()) {
5261 case Work::kHorizontalLine_Segment:
5262 pts = HQuadIntersect(wt.pts(), wn.left(),
5263 wn.right(), wn.y(), wn.xFlipped(), ts);
5264 break;
5265 case Work::kVerticalLine_Segment:
5266 pts = VQuadIntersect(wt.pts(), wn.top(),
5267 wn.bottom(), wn.x(), wn.yFlipped(), ts);
5268 break;
5269 case Work::kLine_Segment: {
5270 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005271 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005272 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005273 break;
5274 }
5275 case Work::kQuad_Segment: {
5276 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00005277 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005278 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005279 break;
5280 }
5281 case Work::kCubic_Segment: {
5282 wt.promoteToCubic();
5283 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
5284 break;
5285 }
5286 default:
5287 SkASSERT(0);
5288 }
5289 break;
5290 case Work::kCubic_Segment:
5291 switch (wn.segmentType()) {
5292 case Work::kHorizontalLine_Segment:
5293 pts = HCubicIntersect(wt.pts(), wn.left(),
5294 wn.right(), wn.y(), wn.xFlipped(), ts);
5295 break;
5296 case Work::kVerticalLine_Segment:
5297 pts = VCubicIntersect(wt.pts(), wn.top(),
5298 wn.bottom(), wn.x(), wn.yFlipped(), ts);
5299 break;
5300 case Work::kLine_Segment: {
5301 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
5302 break;
5303 }
5304 case Work::kQuad_Segment: {
5305 wn.promoteToCubic();
5306 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
5307 break;
5308 }
5309 case Work::kCubic_Segment: {
5310 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
5311 break;
5312 }
5313 default:
5314 SkASSERT(0);
5315 }
5316 break;
5317 default:
5318 SkASSERT(0);
5319 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005320 if (!foundCommonContour && pts > 0) {
5321 test->addCross(next);
5322 next->addCross(test);
5323 foundCommonContour = true;
5324 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005325 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00005326 if (pts == 2) {
5327 if (wn.segmentType() <= Work::kLine_Segment
5328 && wt.segmentType() <= Work::kLine_Segment) {
5329 wt.addCoincident(wn, ts, swap);
5330 continue;
5331 }
5332 if (wn.segmentType() == Work::kQuad_Segment
5333 && wt.segmentType() == Work::kQuad_Segment
5334 && ts.coincidentUsed() == 2) {
5335 wt.addCoincident(wn, ts, swap);
5336 continue;
5337 }
5338
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00005339 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00005340 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005341 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
5342 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005343 int testTAt = wt.addT(ts.fT[swap][pt], wn);
5344 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005345 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
5346 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005347 }
5348 } while (wn.advance());
5349 } while (wt.advance());
5350 return true;
5351}
5352
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005353// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005354// see if coincidence is formed by clipping non-concident segments
caryclark@google.com4eeda372012-12-06 21:47:48 +00005355static void coincidenceCheck(SkTDArray<Contour*>& contourList, int total) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005356 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00005357 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005358 Contour* contour = contourList[cIndex];
caryclark@google.com7ba591e2012-11-20 14:21:54 +00005359 contour->resolveCoincidence(contourList);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005360 }
5361 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5362 Contour* contour = contourList[cIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00005363 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005364 }
5365}
5366
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005367#if CHECK_IN_X
5368static int contourRangeCheckX(SkTDArray<Contour*>& contourList, Segment*& current, int& index,
5369 int& endIndex, double& bestHit, bool& tryAgain, double mid, bool opp) {
5370 SkPoint basePt;
5371 current->xyAtT(index, endIndex, mid, basePt);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005372 int contourCount = contourList.count();
5373 SkScalar bestX = SK_ScalarMin;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005374 Segment* bestSeg = NULL;
5375 int bestTIndex;
5376 bool bestOpp;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005377 for (int cTest = 0; cTest < contourCount; ++cTest) {
5378 Contour* contour = contourList[cTest];
5379 bool testOpp = contour->operand() ^ current->operand() ^ opp;
5380 if (basePt.fX < contour->bounds().fLeft) {
5381 continue;
5382 }
5383 if (bestX > contour->bounds().fRight) {
5384 continue;
5385 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005386 int segmentCount = contour->segments().count();
5387 for (int test = 0; test < segmentCount; ++test) {
5388 Segment* testSeg = &contour->segments()[test];
5389 SkScalar testX = bestX;
5390 double testHit;
5391 int testTIndex = testSeg->crossedSpanX(basePt, testX, testHit, testOpp);
5392 if (testTIndex < 0) {
5393 continue;
5394 }
5395 if (testSeg == current && current->betweenTs(index, testHit, endIndex)) {
5396 continue;
5397 }
5398 bestSeg = testSeg;
5399 bestHit = testHit;
5400 bestOpp = testOpp;
5401 bestTIndex = testTIndex;
5402 bestX = testX;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005403 }
5404 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005405 if (!bestSeg) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005406 return 0;
5407 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005408 bool zeroDx = bestSeg->windSum(bestTIndex) == SK_MinS32;
5409 int result;
5410 if (!zeroDx) {
5411 int tryAnother = bestSeg->windingAtTX(bestHit, bestTIndex, bestOpp);
5412 result = bestSeg->windingAtT(bestHit, bestTIndex, bestOpp, zeroDx);
5413 if (result != tryAnother) {
5414 SkDebugf("%s result=%d tryAnother=%d\n", __FUNCTION__, result,
5415 tryAnother);
5416 }
5417 result = tryAnother;
5418 zeroDx = false;
5419 }
5420 if (zeroDx) {
5421 current = bestSeg;
5422 index = bestTIndex;
5423 endIndex = bestSeg->nextSpan(bestTIndex, 1);
5424 tryAgain = true;
5425 return 0;
5426 }
5427 return result;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005428}
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005429#endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005430
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005431static int contourRangeCheckY(SkTDArray<Contour*>& contourList, Segment*& current, int& index,
5432 int& endIndex, double& bestHit, bool& tryAgain, double mid, bool opp) {
5433 SkPoint basePt;
5434 current->xyAtT(index, endIndex, mid, basePt);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005435 int contourCount = contourList.count();
5436 SkScalar bestY = SK_ScalarMin;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005437 Segment* bestSeg = NULL;
5438 int bestTIndex;
5439 bool bestOpp;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005440 for (int cTest = 0; cTest < contourCount; ++cTest) {
5441 Contour* contour = contourList[cTest];
5442 bool testOpp = contour->operand() ^ current->operand() ^ opp;
5443 if (basePt.fY < contour->bounds().fTop) {
5444 continue;
5445 }
5446 if (bestY > contour->bounds().fBottom) {
5447 continue;
5448 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005449 int segmentCount = contour->segments().count();
5450 for (int test = 0; test < segmentCount; ++test) {
5451 Segment* testSeg = &contour->segments()[test];
5452 SkScalar testY = bestY;
5453 double testHit;
5454 int testTIndex = testSeg->crossedSpanY(basePt, testY, testHit, testOpp);
5455 if (testTIndex < 0) {
5456 continue;
5457 }
5458 if (testSeg == current && current->betweenTs(index, testHit, endIndex)) {
5459 continue;
5460 }
5461 bestSeg = testSeg;
5462 bestHit = testHit;
5463 bestOpp = testOpp;
5464 bestTIndex = testTIndex;
5465 bestY = testY;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005466 }
5467 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005468 if (!bestSeg) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005469 return 0;
5470 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005471 if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
5472 current = bestSeg;
5473 index = bestTIndex;
5474 endIndex = bestSeg->nextSpan(bestTIndex, 1);
5475 tryAgain = true;
5476 return 0;
5477 }
5478 bool zeroDx = false;
5479 int tryAnother = bestSeg->windingAtTX(bestHit, bestTIndex, bestOpp);
5480 int result = bestSeg->windingAtT(bestHit, bestTIndex, bestOpp, zeroDx);
5481 if (result != tryAnother) {
5482 SkDebugf("%s result=%d tryAnother=%d\n", __FUNCTION__, result,
5483 tryAnother);
5484 }
5485 SkASSERT(!zeroDx);
5486 return result;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005487}
5488
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005489// project a ray from the top of the contour up and see if it hits anything
5490// note: when we compute line intersections, we keep track of whether
5491// two contours touch, so we need only look at contours not touching this one.
5492// OPTIMIZATION: sort contourList vertically to avoid linear walk
5493static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com31143cf2012-11-09 22:14:19 +00005494 const Segment* current, int index, int endIndex, bool opp) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005495 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005496 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005497 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00005498 const Segment* test = NULL;
5499 int tIndex;
5500 double tHit;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005501 bool crossOpp;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005502 for (int cTest = 0; cTest < contourCount; ++cTest) {
5503 Contour* contour = contourList[cTest];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005504 bool testOpp = contour->operand() ^ current->operand() ^ opp;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005505 if (basePt.fY < contour->bounds().fTop) {
5506 continue;
5507 }
5508 if (bestY > contour->bounds().fBottom) {
5509 continue;
5510 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005511 const Segment* next = contour->crossedSegmentY(basePt, bestY, tIndex, tHit, testOpp);
caryclark@google.com47580692012-07-23 12:14:49 +00005512 if (next) {
5513 test = next;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005514 crossOpp = testOpp;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005515 }
caryclark@google.com47580692012-07-23 12:14:49 +00005516 }
5517 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00005518 return 0;
5519 }
5520 int winding, windValue;
5521 // If the ray hit the end of a span, we need to construct the wheel of
5522 // angles to find the span closest to the ray -- even if there are just
5523 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005524 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00005525 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00005526 SkTDArray<Angle> angles;
5527 int end = test->nextSpan(tIndex, 1);
5528 if (end < 0) {
5529 end = test->nextSpan(tIndex, -1);
5530 }
5531 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00005532 SkASSERT(angles.count() > 0);
5533 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
5534#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00005535 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00005536#endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005537 return SK_MinS32;
caryclark@google.com59823f72012-08-09 18:17:47 +00005538 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005539 test->buildAngles(tIndex, angles, false);
caryclark@google.com47580692012-07-23 12:14:49 +00005540 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00005541 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00005542 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00005543 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00005544 // hour after 6 o'clock
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005545 bool sortable = Segment::SortAngles(angles, sorted);
5546 if (!sortable) {
5547 return SK_MinS32;
5548 }
caryclark@google.com47580692012-07-23 12:14:49 +00005549#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005550 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00005551#endif
5552 // walk the sorted angle fan to find the lowest angle
5553 // above the base point. Currently, the first angle in the sorted array
5554 // is 12 noon or an earlier hour (the next counterclockwise)
5555 int count = sorted.count();
5556 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005557 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00005558 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005559 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00005560 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00005561 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005562 if (angle->unsortable()) {
5563 continue;
5564 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005565 if (baseMatches && angle->isHorizontal()) {
5566 continue;
5567 }
5568 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00005569 test = angle->segment();
5570 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
5571 const SkPoint* pts = test->pts();
5572 indexDx = pts[2].fX - pts[1].fX - indexDx;
5573 }
caryclark@google.com47580692012-07-23 12:14:49 +00005574 if (indexDx < 0) {
5575 left = index;
5576 } else if (indexDx > 0) {
5577 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00005578 int previous = index - 1;
5579 if (previous < 0) {
5580 previous = count - 1;
5581 }
5582 const Angle* prev = sorted[previous];
5583 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
5584#if DEBUG_SORT
5585 SkDebugf("%s use prev\n", __FUNCTION__);
5586#endif
5587 right = previous;
5588 }
caryclark@google.com47580692012-07-23 12:14:49 +00005589 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005590 } else {
5591 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005592 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005593 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005594 if ((left < 0 || right < 0) && mid >= 0) {
5595 angle = sorted[mid];
5596 Segment* midSeg = angle->segment();
5597 int end = angle->end();
5598 if (midSeg->unsortable(end)) {
5599 return SK_MinS32;
5600 }
5601 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005602 if (left < 0 && right < 0) {
5603 left = mid;
5604 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005605 if (left < 0 && right < 0) {
5606 SkASSERT(0);
5607 return SK_MinS32; // unsortable
5608 }
caryclark@google.com47580692012-07-23 12:14:49 +00005609 if (left < 0) {
5610 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005611 } else if (left >= 0 && mid >= 0 && right >= 0
5612 && sorted[mid]->sign() == sorted[right]->sign()) {
5613 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005614 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005615 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00005616 test = angle->segment();
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005617 winding = crossOpp ? test->oppSum(angle) : test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00005618 SkASSERT(winding != SK_MinS32);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005619 windValue = crossOpp ? test->oppValue(angle) : test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00005620#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005621 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
5622 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00005623#endif
5624 } else {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005625 winding = crossOpp ? test->oppSum(tIndex) : test->windSum(tIndex);
5626 if (winding == SK_MinS32) {
5627 return SK_MinS32; // unsortable
5628 }
5629 windValue = crossOpp ? test->oppValue(tIndex) : test->windValue(tIndex);
caryclark@google.com47580692012-07-23 12:14:49 +00005630#if DEBUG_WINDING
5631 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
5632 windValue);
5633#endif
5634 }
5635 // see if a + change in T results in a +/- change in X (compute x'(T))
5636 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00005637 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
5638 const SkPoint* pts = test->pts();
5639 dx = pts[2].fX - pts[1].fX - dx;
5640 }
caryclark@google.com47580692012-07-23 12:14:49 +00005641#if DEBUG_WINDING
5642 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
5643#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00005644 SkASSERT(dx != 0);
5645 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00005646 winding += dx > 0 ? -windValue : windValue;
5647#if DEBUG_WINDING
5648 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
5649#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005650 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005651 return winding;
5652}
rmistry@google.comd6176b02012-08-23 18:14:13 +00005653
caryclark@google.com24bec792012-08-20 12:43:57 +00005654static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
5655 int contourCount = contourList.count();
5656 Segment* result;
5657 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5658 Contour* contour = contourList[cIndex];
5659 result = contour->undoneSegment(start, end);
5660 if (result) {
5661 return result;
5662 }
5663 }
5664 return NULL;
5665}
5666
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005667#define OLD_FIND_CHASE 1
caryclark@google.com24bec792012-08-20 12:43:57 +00005668
caryclark@google.com31143cf2012-11-09 22:14:19 +00005669static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005670 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005671 Span* span;
5672 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005673 const Span& backPtr = span->fOther->span(span->fOtherIndex);
5674 Segment* segment = backPtr.fOther;
5675 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005676 SkTDArray<Angle> angles;
5677 int done = 0;
5678 if (segment->activeAngle(tIndex, done, angles)) {
5679 Angle* last = angles.end() - 1;
5680 tIndex = last->start();
5681 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005682 #if TRY_ROTATE
5683 *chase.insert(0) = span;
5684 #else
5685 *chase.append() = span;
5686 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005687 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005688 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005689 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00005690 continue;
5691 }
5692 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005693 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005694 int angleCount = sorted.count();
caryclark@google.com03f97062012-08-21 13:13:52 +00005695#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005696 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00005697#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005698 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005699 continue;
5700 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005701 // find first angle, initialize winding to computed fWindSum
5702 int firstIndex = -1;
5703 const Angle* angle;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005704#if OLD_FIND_CHASE
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005705 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005706 do {
5707 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005708 segment = angle->segment();
5709 winding = segment->windSum(angle);
5710 } while (winding == SK_MinS32);
5711 int spanWinding = segment->spanSign(angle->start(), angle->end());
5712 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005713 SkDebugf("%s winding=%d spanWinding=%d\n",
5714 __FUNCTION__, winding, spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00005715 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005716 // turn span winding into contour winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005717 if (spanWinding * winding < 0) {
5718 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005719 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005720 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005721 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005722 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005723 // we care about first sign and whether wind sum indicates this
5724 // edge is inside or outside. Maybe need to pass span winding
5725 // or first winding or something into this function?
5726 // advance to first undone angle, then return it and winding
5727 // (to set whether edges are active or not)
5728 int nextIndex = firstIndex + 1;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005729 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005730 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00005731 winding -= angle->segment()->spanSign(angle);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005732#else
5733 do {
5734 angle = sorted[++firstIndex];
5735 segment = angle->segment();
5736 } while (segment->windSum(angle) == SK_MinS32);
5737 #if DEBUG_SORT
5738 segment->debugShowSort(__FUNCTION__, sorted, firstIndex);
5739 #endif
5740 int sumWinding = segment->updateWindingReverse(angle);
5741 int nextIndex = firstIndex + 1;
5742 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
5743 Segment* first = NULL;
5744#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005745 do {
5746 SkASSERT(nextIndex != firstIndex);
5747 if (nextIndex == angleCount) {
5748 nextIndex = 0;
5749 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005750 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00005751 segment = angle->segment();
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005752#if OLD_FIND_CHASE
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005753 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00005754 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005755 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00005756 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
5757 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005758 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005759 tIndex = angle->start();
5760 endIndex = angle->end();
5761 int lesser = SkMin32(tIndex, endIndex);
5762 const Span& nextSpan = segment->span(lesser);
5763 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005764#if 1
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005765 // FIXME: this be wrong? assign startWinding if edge is in
caryclark@google.com9764cc62012-07-12 19:29:45 +00005766 // same direction. If the direction is opposite, winding to
5767 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00005768 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005769 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005770 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005771 segment->markAndChaseWinding(angle, maxWinding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005772#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005773 break;
5774 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005775#else
5776 int start = angle->start();
5777 int end = angle->end();
5778 int maxWinding;
5779 segment->setUpWinding(start, end, maxWinding, sumWinding);
5780 if (!segment->done(angle)) {
5781 if (!first) {
5782 first = segment;
5783 tIndex = start;
5784 endIndex = end;
5785 }
5786 (void) segment->markAngle(maxWinding, sumWinding, true, angle);
5787 }
5788#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005789 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005790 #if TRY_ROTATE
5791 *chase.insert(0) = span;
5792 #else
5793 *chase.append() = span;
5794 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005795 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005796 }
5797 return NULL;
5798}
5799
caryclark@google.com027de222012-07-12 12:52:50 +00005800#if DEBUG_ACTIVE_SPANS
5801static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005802 int index;
5803 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00005804 contourList[index]->debugShowActiveSpans();
5805 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005806 for (index = 0; index < contourList.count(); ++ index) {
5807 contourList[index]->validateActiveSpans();
5808 }
caryclark@google.com027de222012-07-12 12:52:50 +00005809}
5810#endif
5811
caryclark@google.com27c449a2012-07-27 18:26:38 +00005812static bool windingIsActive(int winding, int spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005813 // FIXME: !spanWinding test must be superflorous, true?
caryclark@google.com27c449a2012-07-27 18:26:38 +00005814 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
5815 && (!winding || !spanWinding || winding == -spanWinding);
5816}
5817
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005818static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005819 int& endIndex, SkPoint& topLeft, bool& unsortable, bool& done, bool onlySortable) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005820 Segment* result;
5821 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00005822 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005823 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00005824 Segment* topStart = NULL;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005825 done = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00005826 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5827 Contour* contour = contourList[cIndex];
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005828 if (contour->done()) {
5829 continue;
5830 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005831 const Bounds& bounds = contour->bounds();
5832 if (bounds.fBottom < topLeft.fY) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005833 done = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005834 continue;
5835 }
5836 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005837 done = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005838 continue;
5839 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005840 contour->topSortableSegment(topLeft, bestXY, topStart);
5841 if (!contour->done()) {
5842 done = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005843 }
5844 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005845 if (!topStart) {
5846 return NULL;
5847 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005848 topLeft = bestXY;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005849 result = topStart->findTop(index, endIndex, unsortable, onlySortable);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005850 } while (!result);
5851 return result;
5852}
caryclark@google.com31143cf2012-11-09 22:14:19 +00005853
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005854static int updateWindings(const Segment* current, int index, int endIndex, int& spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005855 int lesser = SkMin32(index, endIndex);
5856 spanWinding = current->spanSign(index, endIndex);
5857 int winding = current->windSum(lesser);
5858 bool inner = useInnerWinding(winding - spanWinding, winding);
5859#if DEBUG_WINDING
5860 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
5861 " inner=%d result=%d\n",
5862 __FUNCTION__, current->debugID(), current->t(lesser),
5863 spanWinding, winding, SkSign32(index - endIndex),
5864 useInnerWinding(winding - spanWinding, winding),
5865 inner ? winding - spanWinding : winding);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005866#endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005867 if (inner) {
5868 winding -= spanWinding;
5869 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005870 return winding;
5871}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005872
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005873typedef int (*RangeChecker)(SkTDArray<Contour*>& contourList, Segment*& current,
5874 int& index, int& endIndex, double& tHit, bool& tryAgain, double mid, bool opp);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005875
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005876static int rightAngleWinding(SkTDArray<Contour*>& contourList,
5877 Segment*& current, int& index, int& endIndex, double& tHit, bool& checkInX, bool& tryAgain,
5878 bool opp) {
5879#if CHECK_IN_X
5880 RangeChecker checker = checkInX ? contourRangeCheckX : contourRangeCheckY;
5881#else
5882 RangeChecker checker = contourRangeCheckY;
5883#endif
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005884 double test = 0.9;
5885 int contourWinding;
5886 do {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005887 contourWinding = (*checker)(contourList, current, index, endIndex, tHit, tryAgain, test,
5888 opp);
5889 if (contourWinding != SK_MinS32 || tryAgain) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005890 return contourWinding;
5891 }
5892 test /= 2;
5893 } while (!approximately_negative(test));
5894 SkASSERT(0); // should be OK to comment out, but interested when this hits
5895 return contourWinding;
5896}
5897
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005898static void skipVertical(SkTDArray<Contour*>& contourList,
5899 Segment*& current, int& index, int& endIndex) {
5900 if (!current->isVertical(index, endIndex)) {
5901 return;
5902 }
5903 int contourCount = contourList.count();
5904 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5905 Contour* contour = contourList[cIndex];
5906 if (contour->done()) {
5907 continue;
5908 }
5909 current = contour->nonVerticalSegment(index, endIndex);
5910 if (current) {
5911 return;
5912 }
5913 }
5914}
5915
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005916static Segment* tryRightAngleRay(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005917 int& endIndex, SkPoint& topLeft, bool& unsortable, bool& checkInX) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005918 // the simple upward projection of the unresolved points hit unsortable angles
5919 // shoot rays at right angles to the segment to find its winding, ignoring angle cases
5920 topLeft.fX = topLeft.fY = SK_ScalarMin;
5921 Segment* current;
5922 do {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005923 bool done;
5924 current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, done, false);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005925 SkASSERT(current); // FIXME: return to caller that path cannot be simplified (yet)
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005926 checkInX = current->moreHorizontal(index, endIndex, unsortable);
5927 } while (unsortable);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005928 return current;
5929}
5930
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005931static Segment* findSortableTopOld(SkTDArray<Contour*>& contourList, bool& firstContour, int& index,
5932 int& endIndex, SkPoint& topLeft, int& contourWinding, bool& unsortable) {
5933 Segment* current;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005934 do {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005935 bool done;
5936 current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, done, true);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005937 if (!current) {
5938 break;
5939 }
5940 if (firstContour) {
5941 contourWinding = 0;
5942 firstContour = false;
5943 break;
5944 }
5945 int sumWinding = current->windSum(SkMin32(index, endIndex));
5946 // FIXME: don't I have to adjust windSum to get contourWinding?
5947 if (sumWinding == SK_MinS32) {
5948 sumWinding = current->computeSum(index, endIndex, false);
5949 }
5950 if (sumWinding == SK_MinS32) {
5951 contourWinding = innerContourCheck(contourList, current, index, endIndex, false);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005952 } else {
5953 contourWinding = sumWinding;
5954 int spanWinding = current->spanSign(index, endIndex);
5955 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
5956 if (inner) {
5957 contourWinding -= spanWinding;
5958 }
5959#if DEBUG_WINDING
5960 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n",
5961 __FUNCTION__, sumWinding, spanWinding, SkSign32(index - endIndex),
5962 inner, contourWinding);
5963#endif
5964 }
5965 } while (contourWinding == SK_MinS32);
5966 if (contourWinding != SK_MinS32) {
5967#if DEBUG_WINDING
5968 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
5969#endif
5970 return current;
5971 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005972 bool checkInX;
5973 current = tryRightAngleRay(contourList, index, endIndex, topLeft, unsortable, checkInX);
5974 bool tryAgain = false;
5975 double tHit;
5976 do {
5977 contourWinding = rightAngleWinding(contourList, current, index, endIndex, tHit,
5978 checkInX, tryAgain, false);
5979 } while (tryAgain);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005980 return current;
5981}
5982
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005983// Each segment may have an inside or an outside. Segments contained within
5984// winding may have insides on either side, and form a contour that should be
5985// ignored. Segments that are coincident with opposing direction segments may
5986// have outsides on either side, and should also disappear.
5987// 'Normal' segments will have one inside and one outside. Subsequent connections
5988// when winding should follow the intersection direction. If more than one edge
5989// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005990 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00005991 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005992// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005993static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005994 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005995 bool unsortable = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005996 bool topUnsortable = false;
5997 bool firstRetry = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005998 bool closable = true;
5999 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com15fa1382012-05-07 20:49:36 +00006000 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00006001 int index, endIndex;
caryclark@google.com31143cf2012-11-09 22:14:19 +00006002 // iterates while top is unsortable
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00006003 int contourWinding;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006004 Segment* current = findSortableTopOld(contourList, firstContour, index, endIndex, topLeft,
6005 contourWinding, topUnsortable);
6006 if (!current) {
6007 if (topUnsortable) {
6008 topUnsortable = false;
6009 SkASSERT(!firstRetry);
6010 firstRetry = true;
6011 topLeft.fX = topLeft.fY = SK_ScalarMin;
6012 continue;
caryclark@google.com200c2112012-08-03 15:05:04 +00006013 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006014 break;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00006015 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00006016 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00006017 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00006018 // FIXME: needs work. While it works in limited situations, it does
6019 // not always compute winding correctly. Active should be removed and instead
6020 // the initial winding should be correctly passed in so that if the
6021 // inner contour is wound the same way, it never finds an accumulated
6022 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00006023 // other than zero when resolving sorted angles.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00006024 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00006025 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00006026 bool active = windingIsActive(winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00006027 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00006028 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
6029 __FUNCTION__, active ? "true" : "false",
6030 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00006031 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00006032 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006033 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00006034 int nextStart = index;
6035 int nextEnd = endIndex;
6036 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006037 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00006038 if (!next) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006039 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00006040 && current->verb() != SkPath::kLine_Verb
6041 && !simple.isClosed()) {
6042 current->addCurveTo(index, endIndex, simple, true);
6043 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00006044 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00006045 break;
6046 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006047 #if DEBUG_FLOW
6048 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
6049 current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
6050 current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
6051 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00006052 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00006053 current = next;
6054 index = nextStart;
6055 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00006056 } while (!simple.isClosed()
6057 && ((active && !unsortable) || !current->done()));
6058 if (active) {
6059 if (!simple.isClosed()) {
6060 SkASSERT(unsortable);
6061 int min = SkMin32(index, endIndex);
6062 if (!current->done(min)) {
6063 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006064 current->markDone(min, winding ? winding : spanWinding);
caryclark@google.comf839c032012-10-26 21:03:50 +00006065 }
6066 closable = false;
6067 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00006068 simple.close();
6069 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00006070 current = findChase(chaseArray, index, endIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00006071 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00006072 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00006073 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00006074 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00006075 break;
6076 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00006077 winding = updateWindings(current, index, endIndex, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00006078 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00006079 } while (true);
caryclark@google.comf839c032012-10-26 21:03:50 +00006080 return closable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006081}
6082
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006083static Segment* findSortableTopNew(SkTDArray<Contour*>& contourList, bool& firstContour, int& index,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00006084 int& endIndex, SkPoint& topLeft, bool& unsortable, bool& done, bool binary) {
6085 Segment* current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, done,
6086 true);
6087 if (!current) {
6088 return NULL;
6089 }
6090 if (firstContour) {
6091 current->initWinding(index, endIndex, false, 0, 0);
6092 firstContour = false;
6093 return current;
6094 }
6095 int minIndex = SkMin32(index, endIndex);
6096 int sumWinding = current->windSum(minIndex);
6097 if (sumWinding != SK_MinS32) {
6098 return current;
6099 }
6100 sumWinding = current->computeSum(index, endIndex, binary);
6101 if (sumWinding != SK_MinS32) {
6102 return current;
6103 }
6104 int contourWinding;
6105 int oppContourWinding = 0;
6106#if 1
6107 contourWinding = innerContourCheck(contourList, current, index, endIndex, false);
6108 if (contourWinding != SK_MinS32) {
6109 if (binary) {
6110 oppContourWinding = innerContourCheck(contourList, current, index, endIndex, true);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006111 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00006112 if (!binary || oppContourWinding != SK_MinS32) {
6113 current->initWinding(index, endIndex, false, contourWinding, oppContourWinding);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006114 return current;
6115 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00006116 }
6117#endif
6118 // the simple upward projection of the unresolved points hit unsortable angles
6119 // shoot rays at right angles to the segment to find its winding, ignoring angle cases
6120#if CHECK_IN_X
6121 bool checkInX = current->moreHorizontal(index, endIndex, unsortable);
6122#else
6123 bool checkInX = false;
6124#endif
6125 bool tryAgain;
6126 double tHit;
6127 do {
6128#if !CHECK_IN_X
6129 // if current is vertical, find another candidate which is not
6130 // if only remaining candidates are vertical, then they can be marked done
6131 skipVertical(contourList, current, index, endIndex);
6132#endif
6133 tryAgain = false;
6134 contourWinding = rightAngleWinding(contourList, current, index, endIndex, tHit, checkInX,
6135 tryAgain, false);
6136 if (tryAgain) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006137 continue;
6138 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00006139 if (!binary) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006140 break;
6141 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00006142 oppContourWinding = rightAngleWinding(contourList, current, index, endIndex, tHit, checkInX,
6143 tryAgain, true);
6144 } while (tryAgain);
6145#if CHECK_IN_X
6146 current->initWinding(index, endIndex, checkInX, contourWinding, oppContourWinding);
6147#else
6148 current->initWindingX(index, endIndex, tHit, contourWinding, oppContourWinding);
6149#endif
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006150 return current;
6151}
6152
6153// rewrite that abandons keeping local track of winding
6154static bool bridgeWindingX(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
6155 bool firstContour = true;
6156 bool unsortable = false;
6157 bool topUnsortable = false;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006158 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
6159 do {
6160 int index, endIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00006161 bool topDone;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006162 Segment* current = findSortableTopNew(contourList, firstContour, index, endIndex, topLeft,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00006163 topUnsortable, topDone, false);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006164 if (!current) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00006165 if (topUnsortable || !topDone) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006166 topUnsortable = false;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00006167 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006168 topLeft.fX = topLeft.fY = SK_ScalarMin;
6169 continue;
6170 }
6171 break;
6172 }
6173 SkTDArray<Span*> chaseArray;
6174 do {
6175 if (current->activeWinding(index, endIndex)) {
6176 do {
6177 #if DEBUG_ACTIVE_SPANS
6178 if (!unsortable && current->done()) {
6179 debugShowActiveSpans(contourList);
6180 }
6181 #endif
6182 SkASSERT(unsortable || !current->done());
6183 int nextStart = index;
6184 int nextEnd = endIndex;
6185 Segment* next = current->findNextWinding(chaseArray, nextStart, nextEnd,
6186 unsortable);
6187 if (!next) {
6188 if (!unsortable && simple.hasMove()
6189 && current->verb() != SkPath::kLine_Verb
6190 && !simple.isClosed()) {
6191 current->addCurveTo(index, endIndex, simple, true);
6192 SkASSERT(simple.isClosed());
6193 }
6194 break;
6195 }
6196 current->addCurveTo(index, endIndex, simple, true);
6197 current = next;
6198 index = nextStart;
6199 endIndex = nextEnd;
6200 } while (!simple.isClosed() && ((!unsortable) || !current->done()));
6201 if (current->activeWinding(index, endIndex) && !simple.isClosed()) {
6202 SkASSERT(unsortable);
6203 int min = SkMin32(index, endIndex);
6204 if (!current->done(min)) {
6205 current->addCurveTo(index, endIndex, simple, true);
6206 current->markDoneUnary(min);
6207 }
6208 }
6209 simple.close();
6210 } else {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00006211 Span* last = current->markAndChaseDoneUnary(index, endIndex);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006212 if (last) {
6213 *chaseArray.append() = last;
6214 }
6215 }
6216 current = findChase(chaseArray, index, endIndex);
6217 #if DEBUG_ACTIVE_SPANS
6218 debugShowActiveSpans(contourList);
6219 #endif
6220 if (!current) {
6221 break;
6222 }
6223 } while (true);
6224 } while (true);
6225 return simple.someAssemblyRequired();
6226}
6227
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006228// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00006229static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00006230 Segment* current;
6231 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006232 bool unsortable = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006233 bool closable = true;
caryclark@google.com24bec792012-08-20 12:43:57 +00006234 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00006235 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006236 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00006237 int nextStart = start;
6238 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006239 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00006240 if (!next) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006241 if (!unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00006242 && current->verb() != SkPath::kLine_Verb
6243 && !simple.isClosed()) {
6244 current->addCurveTo(start, end, simple, true);
6245 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00006246 }
caryclark@google.com24bec792012-08-20 12:43:57 +00006247 break;
6248 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006249 #if DEBUG_FLOW
6250 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
6251 current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
6252 current->xyAtT(end).fX, current->xyAtT(end).fY);
6253 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00006254 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00006255 current = next;
6256 start = nextStart;
6257 end = nextEnd;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006258 } while (!simple.isClosed() && (!unsortable || !current->done()));
6259 if (!simple.isClosed()) {
6260 SkASSERT(unsortable);
6261 int min = SkMin32(start, end);
6262 if (!current->done(min)) {
6263 current->addCurveTo(start, end, simple, true);
6264 current->markDone(min, 1);
6265 }
6266 closable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00006267 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006268 simple.close();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00006269 #if DEBUG_ACTIVE_SPANS
6270 debugShowActiveSpans(contourList);
6271 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00006272 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006273 return closable;
caryclark@google.com24bec792012-08-20 12:43:57 +00006274}
6275
caryclark@google.comb45a1b42012-05-18 20:50:33 +00006276static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
6277 int contourCount = contourList.count();
6278 for (int cTest = 0; cTest < contourCount; ++cTest) {
6279 Contour* contour = contourList[cTest];
6280 contour->fixOtherTIndex();
6281 }
6282}
6283
caryclark@google.comfb51afb2012-10-19 15:54:16 +00006284static void sortSegments(SkTDArray<Contour*>& contourList) {
6285 int contourCount = contourList.count();
6286 for (int cTest = 0; cTest < contourCount; ++cTest) {
6287 Contour* contour = contourList[cTest];
6288 contour->sortSegments();
6289 }
6290}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00006291
caryclark@google.com4eeda372012-12-06 21:47:48 +00006292static void makeContourList(SkTArray<Contour>& contours, SkTDArray<Contour*>& list,
6293 bool evenOdd, bool oppEvenOdd) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00006294 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006295 if (count == 0) {
6296 return;
6297 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00006298 for (int index = 0; index < count; ++index) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00006299 Contour& contour = contours[index];
6300 contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
6301 *list.append() = &contour;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006302 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006303 QSort<Contour>(list.begin(), list.end() - 1);
6304}
6305
caryclark@google.comf839c032012-10-26 21:03:50 +00006306static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006307 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006308}
6309
caryclark@google.comf839c032012-10-26 21:03:50 +00006310 /*
6311 check start and end of each contour
6312 if not the same, record them
6313 match them up
6314 connect closest
6315 reassemble contour pieces into new path
6316 */
6317static void assemble(const PathWrapper& path, PathWrapper& simple) {
6318#if DEBUG_PATH_CONSTRUCTION
6319 SkDebugf("%s\n", __FUNCTION__);
6320#endif
6321 SkTArray<Contour> contours;
6322 EdgeBuilder builder(path, contours);
6323 builder.finish();
6324 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00006325 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00006326 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00006327 for (outer = 0; outer < count; ++outer) {
6328 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00006329 const SkPoint& eStart = eContour.start();
6330 const SkPoint& eEnd = eContour.end();
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006331#if DEBUG_ASSEMBLE
6332 SkDebugf("%s contour", __FUNCTION__);
6333 if (!approximatelyEqual(eStart, eEnd)) {
6334 SkDebugf("[%d]", runs.count());
6335 } else {
6336 SkDebugf(" ");
6337 }
skia.committer@gmail.com61b05dc2012-12-14 02:02:06 +00006338 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006339 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
6340#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00006341 if (approximatelyEqual(eStart, eEnd)) {
6342 eContour.toPath(simple);
6343 continue;
6344 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00006345 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00006346 }
6347 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00006348 if (count == 0) {
6349 return;
6350 }
caryclark@google.comf839c032012-10-26 21:03:50 +00006351 SkTDArray<int> sLink, eLink;
6352 sLink.setCount(count);
6353 eLink.setCount(count);
caryclark@google.com0b7da432012-10-31 19:00:20 +00006354 SkTDArray<double> sBest, eBest;
6355 sBest.setCount(count);
6356 eBest.setCount(count);
caryclark@google.comf839c032012-10-26 21:03:50 +00006357 int rIndex;
6358 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006359 outer = runs[rIndex];
6360 const Contour& oContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00006361 const SkPoint& oStart = oContour.start();
6362 const SkPoint& oEnd = oContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00006363 double dx = oEnd.fX - oStart.fX;
6364 double dy = oEnd.fY - oStart.fY;
6365 double dist = dx * dx + dy * dy;
6366 sBest[rIndex] = eBest[rIndex] = dist;
6367 sLink[rIndex] = eLink[rIndex] = rIndex;
6368 }
6369 for (rIndex = 0; rIndex < count - 1; ++rIndex) {
6370 outer = runs[rIndex];
6371 const Contour& oContour = contours[outer];
6372 const SkPoint& oStart = oContour.start();
6373 const SkPoint& oEnd = oContour.end();
6374 double bestStartDist = sBest[rIndex];
6375 double bestEndDist = eBest[rIndex];
6376 for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) {
6377 int inner = runs[iIndex];
6378 const Contour& iContour = contours[inner];
caryclark@google.comf839c032012-10-26 21:03:50 +00006379 const SkPoint& iStart = iContour.start();
6380 const SkPoint& iEnd = iContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00006381 double dx = iStart.fX - oStart.fX;
6382 double dy = iStart.fY - oStart.fY;
6383 double dist = dx * dx + dy * dy;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006384 if (bestStartDist > dist && sBest[iIndex] > dist) {
6385 sBest[iIndex] = bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00006386 sLink[rIndex] = ~iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00006387 sLink[iIndex] = ~rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006388 }
6389 dx = iEnd.fX - oStart.fX;
6390 dy = iEnd.fY - oStart.fY;
6391 dist = dx * dx + dy * dy;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006392 if (bestStartDist > dist && eBest[iIndex] > dist) {
6393 eBest[iIndex] = bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00006394 sLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00006395 eLink[iIndex] = rIndex;
6396 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00006397 dx = iStart.fX - oEnd.fX;
6398 dy = iStart.fY - oEnd.fY;
6399 dist = dx * dx + dy * dy;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006400 if (bestEndDist > dist && sBest[iIndex] > dist) {
6401 sBest[iIndex] = bestEndDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00006402 sLink[iIndex] = rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006403 eLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00006404 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00006405 dx = iEnd.fX - oEnd.fX;
6406 dy = iEnd.fY - oEnd.fY;
6407 dist = dx * dx + dy * dy;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006408 if (bestEndDist > dist && eBest[iIndex] > dist) {
6409 eBest[iIndex] = bestEndDist = dist;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006410 eLink[iIndex] = ~rIndex;
6411 eLink[rIndex] = ~iIndex;
6412 }
6413 }
caryclark@google.comf839c032012-10-26 21:03:50 +00006414 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006415#if DEBUG_ASSEMBLE
6416 for (rIndex = 0; rIndex < count; ++rIndex) {
6417 int s = sLink[rIndex];
6418 int e = eLink[rIndex];
6419 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
6420 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
caryclark@google.comf839c032012-10-26 21:03:50 +00006421 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006422#endif
6423 rIndex = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00006424 do {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006425 bool forward = true;
6426 bool first = true;
6427 int sIndex = sLink[rIndex];
6428 SkASSERT(sIndex != INT_MAX);
6429 sLink[rIndex] = INT_MAX;
6430 int eIndex;
6431 if (sIndex < 0) {
6432 eIndex = sLink[~sIndex];
6433 sLink[~sIndex] = INT_MAX;
6434 } else {
6435 eIndex = eLink[sIndex];
6436 eLink[sIndex] = INT_MAX;
6437 }
6438 SkASSERT(eIndex != INT_MAX);
6439#if DEBUG_ASSEMBLE
6440 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
skia.committer@gmail.com61b05dc2012-12-14 02:02:06 +00006441 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
6442 eIndex < 0 ? ~eIndex : eIndex);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006443#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00006444 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006445 outer = runs[rIndex];
6446 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00006447 if (first) {
caryclark@google.comf839c032012-10-26 21:03:50 +00006448 first = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006449 const SkPoint* startPtr = &contour.start();
caryclark@google.comf839c032012-10-26 21:03:50 +00006450 simple.deferredMove(startPtr[0]);
6451 }
caryclark@google.comf839c032012-10-26 21:03:50 +00006452 if (forward) {
6453 contour.toPartialForward(simple);
caryclark@google.comf839c032012-10-26 21:03:50 +00006454 } else {
6455 contour.toPartialBackward(simple);
caryclark@google.comf839c032012-10-26 21:03:50 +00006456 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006457#if DEBUG_ASSEMBLE
6458 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
skia.committer@gmail.com61b05dc2012-12-14 02:02:06 +00006459 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006460 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
6461#endif
6462 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
caryclark@google.comf839c032012-10-26 21:03:50 +00006463 simple.close();
caryclark@google.comf839c032012-10-26 21:03:50 +00006464 break;
6465 }
caryclark@google.comf839c032012-10-26 21:03:50 +00006466 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006467 eIndex = eLink[rIndex];
6468 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00006469 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006470 if (eIndex >= 0) {
6471 SkASSERT(sLink[eIndex] == rIndex);
6472 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00006473 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006474 SkASSERT(eLink[~eIndex] == ~rIndex);
6475 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00006476 }
6477 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006478 eIndex = sLink[rIndex];
6479 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00006480 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006481 if (eIndex >= 0) {
6482 SkASSERT(eLink[eIndex] == rIndex);
6483 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00006484 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006485 SkASSERT(sLink[~eIndex] == ~rIndex);
6486 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00006487 }
6488 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00006489 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00006490 if (rIndex < 0) {
6491 forward ^= 1;
6492 rIndex = ~rIndex;
6493 }
6494 } while (true);
6495 for (rIndex = 0; rIndex < count; ++rIndex) {
6496 if (sLink[rIndex] != INT_MAX) {
6497 break;
6498 }
6499 }
6500 } while (rIndex < count);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006501#if DEBUG_ASSEMBLE
6502 for (rIndex = 0; rIndex < count; ++rIndex) {
6503 SkASSERT(sLink[rIndex] == INT_MAX);
6504 SkASSERT(eLink[rIndex] == INT_MAX);
6505 }
6506#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00006507}
6508
6509void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006510 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00006511 result.reset();
6512 result.setFillType(SkPath::kEvenOdd_FillType);
6513 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006514
6515 // turn path into list of segments
6516 SkTArray<Contour> contours;
6517 // FIXME: add self-intersecting cubics' T values to segment
6518 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00006519 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006520 SkTDArray<Contour*> contourList;
caryclark@google.com4eeda372012-12-06 21:47:48 +00006521 makeContourList(contours, contourList, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006522 Contour** currentPtr = contourList.begin();
6523 if (!currentPtr) {
6524 return;
6525 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00006526 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006527 // find all intersections between segments
6528 do {
6529 Contour** nextPtr = currentPtr;
6530 Contour* current = *currentPtr++;
6531 Contour* next;
6532 do {
6533 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00006534 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00006535 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00006536 // eat through coincident edges
caryclark@google.com4eeda372012-12-06 21:47:48 +00006537 coincidenceCheck(contourList, 0);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00006538 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00006539 sortSegments(contourList);
caryclark@google.com0b7da432012-10-31 19:00:20 +00006540#if DEBUG_ACTIVE_SPANS
6541 debugShowActiveSpans(contourList);
6542#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006543 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006544 if (builder.xorMask() == kWinding_Mask
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006545 ? gUseOldBridgeWinding ? !bridgeWinding(contourList, simple)
6546 : bridgeWindingX(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00006547 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006548 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00006549 SkPath temp;
6550 temp.setFillType(SkPath::kEvenOdd_FillType);
6551 PathWrapper assembled(temp);
6552 assemble(simple, assembled);
6553 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00006554 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006555}
6556