blob: 5fd0fd522384773c6dab1c30f31c67d9008a16d2 [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
caryclark@google.com47580692012-07-23 12:14:49 +000020// An Edge is a Segment generated from a Span
caryclark@google.com15fa1382012-05-07 20:49:36 +000021
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022// FIXME: remove once debugging is complete
caryclark@google.com47580692012-07-23 12:14:49 +000023#ifdef SK_DEBUG
24int gDebugMaxWindSum = SK_MaxS32;
25int gDebugMaxWindValue = SK_MaxS32;
26#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +000027
caryclark@google.comf839c032012-10-26 21:03:50 +000028#define PIN_ADD_T 0
caryclark@google.com0b7da432012-10-31 19:00:20 +000029#define TRY_ROTATE 1
caryclark@google.com8f9f4682013-01-03 21:18:16 +000030#define ONE_PASS_COINCIDENCE_CHECK 0
caryclark@google.com73ca6242013-01-17 21:02:47 +000031#define APPROXIMATE_CUBICS 1
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.com73ca6242013-01-17 21:02:47 +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.com8f9f4682013-01-03 21:18:16 +000053#define DEBUG_UNSORTABLE 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000054#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000055#define DEBUG_WINDING 0
caryclark@google.com8f9f4682013-01-03 21:18:16 +000056#define DEBUG_WINDING_AT_T 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000057
58#else
59
caryclark@google.com47580692012-07-23 12:14:49 +000060const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000061
caryclark@google.comc91dfe42012-10-16 12:06:27 +000062#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com4eeda372012-12-06 21:47:48 +000063#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000064#define DEBUG_ADD_INTERSECTING_TS 1
65#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000066#define DEBUG_ANGLE 1
caryclark@google.come7bd5f42012-12-13 19:47:53 +000067#define DEBUG_ASSEMBLE 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000068#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000069#define DEBUG_CROSS 0
caryclark@google.come7bd5f42012-12-13 19:47:53 +000070#define DEBUG_FLOW 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000071#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000072#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com729e1c42012-11-21 21:36:34 +000073#define DEBUG_SHOW_WINDING 0
caryclark@google.com47580692012-07-23 12:14:49 +000074#define DEBUG_SORT 1
caryclark@google.com8f9f4682013-01-03 21:18:16 +000075#define DEBUG_UNSORTABLE 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000076#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000077#define DEBUG_WINDING 1
caryclark@google.com8f9f4682013-01-03 21:18:16 +000078#define DEBUG_WINDING_AT_T 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000079
80#endif
81
caryclark@google.com6aea33f2012-10-09 14:11:58 +000082#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000083
caryclark@google.comfa0588f2012-04-26 21:01:06 +000084#if DEBUG_DUMP
85static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000086// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000087static int gContourID;
88static int gSegmentID;
89#endif
90
caryclark@google.com8dcf1142012-07-02 20:27:02 +000091#ifndef DEBUG_TEST
92#define DEBUG_TEST 0
93#endif
94
caryclark@google.com32546db2012-08-31 20:55:07 +000095#define MAKE_CONST_LINE(line, pts) \
96 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
97#define MAKE_CONST_QUAD(quad, pts) \
98 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
99 {pts[2].fX, pts[2].fY}}
100#define MAKE_CONST_CUBIC(cubic, pts) \
101 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
102 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
103
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000104static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
105 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000106 MAKE_CONST_LINE(aLine, a);
107 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000108 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
109}
110
111static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
112 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000113 MAKE_CONST_QUAD(aQuad, a);
114 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000115 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000116}
117
caryclark@google.com32546db2012-08-31 20:55:07 +0000118static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000119 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000120 MAKE_CONST_CUBIC(aCubic, a);
121 MAKE_CONST_LINE(bLine, b);
caryclark@google.com73ca6242013-01-17 21:02:47 +0000122 return intersect(aCubic, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000123}
124
125static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
126 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000127 MAKE_CONST_QUAD(aQuad, a);
128 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000129#define TRY_QUARTIC_SOLUTION 1
130#if TRY_QUARTIC_SOLUTION
131 intersect2(aQuad, bQuad, intersections);
132#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000133 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000134#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000135 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000136}
137
caryclark@google.com73ca6242013-01-17 21:02:47 +0000138#if APPROXIMATE_CUBICS
139static int CubicQuadIntersect(const SkPoint a[4], const SkPoint b[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000140 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000141 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com73ca6242013-01-17 21:02:47 +0000142 MAKE_CONST_QUAD(bQuad, b);
143 return intersect(aCubic, bQuad, intersections);
144}
145#endif
146
147static int CubicIntersect(const SkPoint a[4], const SkPoint b[4], Intersections& intersections) {
148 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com32546db2012-08-31 20:55:07 +0000149 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.com73ca6242013-01-17 21:02:47 +0000150#if APPROXIMATE_CUBICS
151 intersect2(aCubic, bCubic, intersections);
152#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000153 intersect(aCubic, bCubic, intersections);
caryclark@google.com73ca6242013-01-17 21:02:47 +0000154#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000155 return intersections.fUsed;
156}
157
158static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
159 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000160 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000161 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
162}
163
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000164static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
165 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000166 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000167 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
168}
169
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000170static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
171 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000172 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000173 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
174}
175
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000176static int (* const HSegmentIntersect[])(const SkPoint [], SkScalar ,
177 SkScalar , SkScalar , bool , Intersections& ) = {
178 NULL,
179 HLineIntersect,
180 HQuadIntersect,
181 HCubicIntersect
182};
183
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000184static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
185 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000186 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000187 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
188}
189
190static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
191 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000192 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000193 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
194}
195
196static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
197 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000198 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000199 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000200}
201
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000202static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
203 SkScalar , SkScalar , bool , Intersections& ) = {
204 NULL,
205 VLineIntersect,
206 VQuadIntersect,
207 VCubicIntersect
208};
209
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000210static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000211 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000212 double x, y;
213 xy_at_t(line, t, x, y);
214 out->fX = SkDoubleToScalar(x);
215 out->fY = SkDoubleToScalar(y);
216}
217
218static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000219 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000220 double x, y;
221 xy_at_t(quad, t, x, y);
222 out->fX = SkDoubleToScalar(x);
223 out->fY = SkDoubleToScalar(y);
224}
225
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000226static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
227 MAKE_CONST_QUAD(quad, a);
228 xy_at_t(quad, t, out->x, out->y);
229}
230
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000231static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000232 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000233 double x, y;
234 xy_at_t(cubic, t, x, y);
235 out->fX = SkDoubleToScalar(x);
236 out->fY = SkDoubleToScalar(y);
237}
238
239static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
240 NULL,
241 LineXYAtT,
242 QuadXYAtT,
243 CubicXYAtT
244};
245
246static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000247 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000248 double x;
249 xy_at_t(aLine, t, x, *(double*) 0);
250 return SkDoubleToScalar(x);
251}
252
253static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000254 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000255 double x;
256 xy_at_t(quad, t, x, *(double*) 0);
257 return SkDoubleToScalar(x);
258}
259
260static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000261 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000262 double x;
263 xy_at_t(cubic, t, x, *(double*) 0);
264 return SkDoubleToScalar(x);
265}
266
267static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
268 NULL,
269 LineXAtT,
270 QuadXAtT,
271 CubicXAtT
272};
273
274static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000275 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000276 double y;
277 xy_at_t(aLine, t, *(double*) 0, y);
278 return SkDoubleToScalar(y);
279}
280
281static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000282 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000283 double y;
284 xy_at_t(quad, t, *(double*) 0, y);
285 return SkDoubleToScalar(y);
286}
287
288static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000289 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000290 double y;
291 xy_at_t(cubic, t, *(double*) 0, y);
292 return SkDoubleToScalar(y);
293}
294
295static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
296 NULL,
297 LineYAtT,
298 QuadYAtT,
299 CubicYAtT
300};
301
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000302static SkScalar LineDXAtT(const SkPoint a[2], double ) {
303 return a[1].fX - a[0].fX;
304}
305
306static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000307 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000308 double x;
309 dxdy_at_t(quad, t, x, *(double*) 0);
310 return SkDoubleToScalar(x);
311}
312
313static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000314 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000315 double x;
316 dxdy_at_t(cubic, t, x, *(double*) 0);
317 return SkDoubleToScalar(x);
318}
319
320static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
321 NULL,
322 LineDXAtT,
323 QuadDXAtT,
324 CubicDXAtT
325};
326
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000327static SkScalar LineDYAtT(const SkPoint a[2], double ) {
328 return a[1].fY - a[0].fY;
329}
330
331static SkScalar QuadDYAtT(const SkPoint a[3], double t) {
332 MAKE_CONST_QUAD(quad, a);
333 double y;
334 dxdy_at_t(quad, t, *(double*) 0, y);
335 return SkDoubleToScalar(y);
336}
337
338static SkScalar CubicDYAtT(const SkPoint a[4], double t) {
339 MAKE_CONST_CUBIC(cubic, a);
340 double y;
341 dxdy_at_t(cubic, t, *(double*) 0, y);
342 return SkDoubleToScalar(y);
343}
344
345static SkScalar (* const SegmentDYAtT[])(const SkPoint [], double ) = {
346 NULL,
347 LineDYAtT,
348 QuadDYAtT,
349 CubicDYAtT
350};
351
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000352static void LineSubDivide(const SkPoint a[2], double startT, double endT,
353 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000354 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000355 _Line dst;
356 sub_divide(aLine, startT, endT, dst);
357 sub[0].fX = SkDoubleToScalar(dst[0].x);
358 sub[0].fY = SkDoubleToScalar(dst[0].y);
359 sub[1].fX = SkDoubleToScalar(dst[1].x);
360 sub[1].fY = SkDoubleToScalar(dst[1].y);
361}
362
363static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
364 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000365 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000366 Quadratic dst;
367 sub_divide(aQuad, startT, endT, dst);
368 sub[0].fX = SkDoubleToScalar(dst[0].x);
369 sub[0].fY = SkDoubleToScalar(dst[0].y);
370 sub[1].fX = SkDoubleToScalar(dst[1].x);
371 sub[1].fY = SkDoubleToScalar(dst[1].y);
372 sub[2].fX = SkDoubleToScalar(dst[2].x);
373 sub[2].fY = SkDoubleToScalar(dst[2].y);
374}
375
376static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
377 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000378 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000379 Cubic dst;
380 sub_divide(aCubic, startT, endT, dst);
381 sub[0].fX = SkDoubleToScalar(dst[0].x);
382 sub[0].fY = SkDoubleToScalar(dst[0].y);
383 sub[1].fX = SkDoubleToScalar(dst[1].x);
384 sub[1].fY = SkDoubleToScalar(dst[1].y);
385 sub[2].fX = SkDoubleToScalar(dst[2].x);
386 sub[2].fY = SkDoubleToScalar(dst[2].y);
387 sub[3].fX = SkDoubleToScalar(dst[3].x);
388 sub[3].fY = SkDoubleToScalar(dst[3].y);
389}
390
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000391static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
392 SkPoint []) = {
393 NULL,
394 LineSubDivide,
395 QuadSubDivide,
396 CubicSubDivide
397};
398
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000399static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000400 _Line sub) {
401 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000402 _Line dst;
403 sub_divide(aLine, startT, endT, dst);
404 sub[0] = dst[0];
405 sub[1] = dst[1];
406}
407
408static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000409 Quadratic sub) {
410 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000411 Quadratic dst;
412 sub_divide(aQuad, startT, endT, dst);
413 sub[0] = dst[0];
414 sub[1] = dst[1];
415 sub[2] = dst[2];
416}
417
418static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000419 Cubic sub) {
420 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000421 Cubic dst;
422 sub_divide(aCubic, startT, endT, dst);
423 sub[0] = dst[0];
424 sub[1] = dst[1];
425 sub[2] = dst[2];
426 sub[3] = dst[3];
427}
428
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000429#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000430static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
431 SkRect& bounds) {
432 SkPoint dst[3];
433 QuadSubDivide(a, startT, endT, dst);
434 bounds.fLeft = bounds.fRight = dst[0].fX;
435 bounds.fTop = bounds.fBottom = dst[0].fY;
436 for (int index = 1; index < 3; ++index) {
437 bounds.growToInclude(dst[index].fX, dst[index].fY);
438 }
439}
440
441static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
442 SkRect& bounds) {
443 SkPoint dst[4];
444 CubicSubDivide(a, startT, endT, dst);
445 bounds.fLeft = bounds.fRight = dst[0].fX;
446 bounds.fTop = bounds.fBottom = dst[0].fY;
447 for (int index = 1; index < 4; ++index) {
448 bounds.growToInclude(dst[index].fX, dst[index].fY);
449 }
450}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000451#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000452
caryclark@google.com15fa1382012-05-07 20:49:36 +0000453static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000454 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000455 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000456 Quadratic dst;
457 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000458 if (order == 2) { // quad became line
459 for (int index = 0; index < order; ++index) {
460 SkPoint* pt = reducePts.append();
461 pt->fX = SkDoubleToScalar(dst[index].x);
462 pt->fY = SkDoubleToScalar(dst[index].y);
463 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000464 }
465 return (SkPath::Verb) (order - 1);
466}
467
468static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
469 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000470 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000471 Cubic dst;
472 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000473 if (order == 2 || order == 3) { // cubic became line or quad
474 for (int index = 0; index < order; ++index) {
475 SkPoint* pt = reducePts.append();
476 pt->fX = SkDoubleToScalar(dst[index].x);
477 pt->fY = SkDoubleToScalar(dst[index].y);
478 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000479 }
480 return (SkPath::Verb) (order - 1);
481}
482
caryclark@google.com15fa1382012-05-07 20:49:36 +0000483static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000484 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000485 return isLinear(aQuad, 0, 2);
486}
487
488static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000489 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000490 return isLinear(aCubic, 0, 3);
491}
492
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000493static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000494 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000495 double x[2];
496 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000497 xy_at_t(aLine, endT, x[1], *(double*) 0);
498 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000499}
500
501static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000502 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000503 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000504}
505
506static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000507 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000508 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000509}
510
511static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
512 NULL,
513 LineLeftMost,
514 QuadLeftMost,
515 CubicLeftMost
516};
517
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000518#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000519static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
520 Intersections& intersections) {
521 MAKE_CONST_QUAD(aQuad, a);
522 MAKE_CONST_LINE(bLine, b);
523 return intersectRay(aQuad, bLine, intersections);
524}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000525#endif
526
527static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
528 Intersections& intersections) {
529 MAKE_CONST_QUAD(aQuad, a);
530 return intersectRay(aQuad, bLine, intersections);
531}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000532
caryclark@google.comdb0b3e02012-12-21 21:34:36 +0000533static bool LineVertical(const SkPoint a[2], double startT, double endT) {
534 MAKE_CONST_LINE(aLine, a);
535 double x[2];
536 xy_at_t(aLine, startT, x[0], *(double*) 0);
537 xy_at_t(aLine, endT, x[1], *(double*) 0);
caryclark@google.com6d0032a2013-01-04 19:41:13 +0000538 return AlmostEqualUlps((float) x[0], (float) x[1]);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +0000539}
540
541static bool QuadVertical(const SkPoint a[3], double startT, double endT) {
542 SkPoint dst[3];
543 QuadSubDivide(a, startT, endT, dst);
caryclark@google.com6d0032a2013-01-04 19:41:13 +0000544 return AlmostEqualUlps(dst[0].fX, dst[1].fX) && AlmostEqualUlps(dst[1].fX, dst[2].fX);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +0000545}
546
547static bool CubicVertical(const SkPoint a[4], double startT, double endT) {
548 SkPoint dst[4];
549 CubicSubDivide(a, startT, endT, dst);
caryclark@google.com6d0032a2013-01-04 19:41:13 +0000550 return AlmostEqualUlps(dst[0].fX, dst[1].fX) && AlmostEqualUlps(dst[1].fX, dst[2].fX)
551 && AlmostEqualUlps(dst[2].fX, dst[3].fX);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +0000552}
553
554static bool (* const SegmentVertical[])(const SkPoint [], double , double) = {
555 NULL,
556 LineVertical,
557 QuadVertical,
558 CubicVertical
559};
560
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000561class Segment;
562
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000563struct Span {
564 Segment* fOther;
565 mutable SkPoint fPt; // lazily computed as needed
566 double fT;
567 double fOtherT; // value at fOther[fOtherIndex].fT
568 int fOtherIndex; // can't be used during intersection
caryclark@google.com31143cf2012-11-09 22:14:19 +0000569 int fWindSum; // accumulated from contours surrounding this one.
570 int fOppSum; // for binary operators: the opposite winding sum
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000571 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000572 int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000573 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000574 bool fUnsortableStart; // set when start is part of an unsortable pair
575 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.comf839c032012-10-26 21:03:50 +0000576 bool fTiny; // if set, span may still be considered once for edge following
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000577};
578
caryclark@google.com15fa1382012-05-07 20:49:36 +0000579// sorting angles
580// given angles of {dx dy ddx ddy dddx dddy} sort them
581class Angle {
582public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000583 // FIXME: this is bogus for quads and cubics
584 // if the quads and cubics' line from end pt to ctrl pt are coincident,
585 // there's no obvious way to determine the curve ordering from the
586 // derivatives alone. In particular, if one quadratic's coincident tangent
587 // is longer than the other curve, the final control point can place the
588 // longer curve on either side of the shorter one.
589 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
590 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000591
caryclark@google.com32546db2012-08-31 20:55:07 +0000592 /*(
593 for quads and cubics, set up a parameterized line (e.g. LineParameters )
594 for points [0] to [1]. See if point [2] is on that line, or on one side
595 or the other. If it both quads' end points are on the same side, choose
596 the shorter tangent. If the tangents are equal, choose the better second
597 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000598
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000599 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000600 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000601 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000602 double y = dy();
603 double ry = rh.dy();
604 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
605 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000606 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000607 double x = dx();
608 double rx = rh.dx();
609 if (y == 0 && ry == 0 && x * rx < 0) {
610 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000611 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000612 double x_ry = x * ry;
613 double rx_y = rx * y;
614 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000615 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000616 return cmp < 0;
617 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000618 if (approximately_zero(x_ry) && approximately_zero(rx_y)
619 && !approximately_zero_squared(cmp)) {
620 return cmp < 0;
621 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000622 // at this point, the initial tangent line is coincident
caryclark@google.com31143cf2012-11-09 22:14:19 +0000623 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
624 || !approximately_zero(rh.fSide))) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000625 // FIXME: running demo will trigger this assertion
626 // (don't know if commenting out will trigger further assertion or not)
627 // commenting it out allows demo to run in release, though
628 // SkASSERT(fSide != rh.fSide);
629 return fSide < rh.fSide;
630 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000631 // see if either curve can be lengthened and try the tangent compare again
632 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
633 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
634 Angle longer = *this;
635 Angle rhLonger = rh;
636 if (longer.lengthen() | rhLonger.lengthen()) {
637 return longer < rhLonger;
638 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +0000639 #if 0
caryclark@google.coma461ff02012-10-11 12:54:23 +0000640 // what if we extend in the other direction?
641 longer = *this;
642 rhLonger = rh;
643 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
644 return longer < rhLonger;
645 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +0000646 #endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000647 }
caryclark@google.com185c7c42012-10-19 18:26:24 +0000648 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
caryclark@google.com31143cf2012-11-09 22:14:19 +0000649 || (rh.fVerb == SkPath::kLine_Verb
650 && approximately_zero(rx) && approximately_zero(ry))) {
caryclark@google.com185c7c42012-10-19 18:26:24 +0000651 // See general unsortable comment below. This case can happen when
652 // one line has a non-zero change in t but no change in x and y.
653 fUnsortable = true;
654 rh.fUnsortable = true;
655 return this < &rh; // even with no solution, return a stable sort
656 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000657 if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny
658 || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) {
659 fUnsortable = true;
660 rh.fUnsortable = true;
661 return this < &rh; // even with no solution, return a stable sort
662 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000663 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
664 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000665 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000666 // end of the shorter tangent to midway between the end points
667 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000668 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
669 double len = fTangent1.normalSquared();
670 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000671 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000672 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000673 int roots, rroots;
674 bool flip = false;
675 do {
676 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
677 double midX = (q[0].x + q[2].x) / 2;
678 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000679 ray[0] = q[1];
680 ray[1].x = midX;
681 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000682 SkASSERT(ray[0] != ray[1]);
683 roots = QuadRayIntersect(fPts, ray, i);
684 rroots = QuadRayIntersect(rh.fPts, ray, ri);
685 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000686 if (roots == 0 || rroots == 0) {
687 // FIXME: we don't have a solution in this case. The interim solution
688 // is to mark the edges as unsortable, exclude them from this and
689 // future computations, and allow the returned path to be fragmented
690 fUnsortable = true;
691 rh.fUnsortable = true;
692 return this < &rh; // even with no solution, return a stable sort
693 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000694 _Point loc;
695 double best = SK_ScalarInfinity;
696 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000697 int index;
698 for (index = 0; index < roots; ++index) {
699 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000700 dx = loc.x - ray[0].x;
701 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000702 dist = dx * dx + dy * dy;
703 if (best > dist) {
704 best = dist;
705 }
706 }
707 for (index = 0; index < rroots; ++index) {
708 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000709 dx = loc.x - ray[0].x;
710 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000711 dist = dx * dx + dy * dy;
712 if (best > dist) {
713 return fSide < 0;
714 }
715 }
716 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000717 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000718
caryclark@google.com47580692012-07-23 12:14:49 +0000719 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000720 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000721 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000722
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000723 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000724 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000725 }
726
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000727 int end() const {
728 return fEnd;
729 }
730
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000731 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000732 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000733 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000734
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000735 bool lengthen() {
736 int newEnd = fEnd;
737 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
738 fEnd = newEnd;
739 setSpans();
740 return true;
741 }
742 return false;
743 }
744
caryclark@google.coma461ff02012-10-11 12:54:23 +0000745 bool reverseLengthen() {
746 if (fReversed) {
747 return false;
748 }
749 int newEnd = fStart;
750 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
751 fEnd = newEnd;
752 fReversed = true;
753 setSpans();
754 return true;
755 }
756 return false;
757 }
758
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000759 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000760 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000761 fSegment = segment;
762 fStart = start;
763 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000764 fPts = orig;
765 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000766 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000767 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000768 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000769 setSpans();
770 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000771
caryclark@google.com8f9f4682013-01-03 21:18:16 +0000772
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000773 void setSpans() {
774 double startT = (*fSpans)[fStart].fT;
775 double endT = (*fSpans)[fEnd].fT;
776 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000777 case SkPath::kLine_Verb:
778 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000779 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000780 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
781 fTangent1.lineEndPoints(l);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000782 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000783 break;
784 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000785 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000786 fTangent1.quadEndPoints(fQ, 0, 1);
787 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000788 break;
789 case SkPath::kCubic_Verb:
790 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000791 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000792 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000793 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000794 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000795 default:
796 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000797 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +0000798 fUnsortable = dx() == 0 && dy() == 0;
caryclark@google.comf839c032012-10-26 21:03:50 +0000799 if (fUnsortable) {
800 return;
801 }
802 SkASSERT(fStart != fEnd);
803 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
804 for (int index = fStart; index != fEnd; index += step) {
caryclark@google.com8f9f4682013-01-03 21:18:16 +0000805#if 1
806 const Span& thisSpan = (*fSpans)[index];
807 const Span& nextSpan = (*fSpans)[index + step];
808 if (thisSpan.fTiny || thisSpan.fT == nextSpan.fT) {
809 continue;
caryclark@google.comf839c032012-10-26 21:03:50 +0000810 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +0000811 fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortableEnd;
812#if DEBUG_UNSORTABLE
813 if (fUnsortable) {
814 SkPoint iPt, ePt;
815 (*SegmentXYAtT[fVerb])(fPts, thisSpan.fT, &iPt);
816 (*SegmentXYAtT[fVerb])(fPts, nextSpan.fT, &ePt);
817 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
818 index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
819 }
820#endif
821 return;
822#else
823 if ((*fSpans)[index].fUnsortableStart) {
caryclark@google.comf839c032012-10-26 21:03:50 +0000824 fUnsortable = true;
825 return;
826 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000827#endif
caryclark@google.comf839c032012-10-26 21:03:50 +0000828 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +0000829#if 1
830#if DEBUG_UNSORTABLE
831 SkPoint iPt, ePt;
832 (*SegmentXYAtT[fVerb])(fPts, startT, &iPt);
833 (*SegmentXYAtT[fVerb])(fPts, endT, &ePt);
834 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
835 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
836#endif
837 fUnsortable = true;
838#endif
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000839 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000840
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000841 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000842 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000843 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000844
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000845 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000846 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000847 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000848
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000849 const SkTDArray<Span>* spans() const {
850 return fSpans;
851 }
852
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000853 int start() const {
854 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000855 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000856
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000857 bool unsortable() const {
858 return fUnsortable;
859 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000860
caryclark@google.comc899ad92012-08-23 15:24:42 +0000861#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000862 const SkPoint* pts() const {
863 return fPts;
864 }
865
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000866 SkPath::Verb verb() const {
867 return fVerb;
868 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000869
caryclark@google.comc899ad92012-08-23 15:24:42 +0000870 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000871 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000872 }
873#endif
874
caryclark@google.com15fa1382012-05-07 20:49:36 +0000875private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000876 const SkPoint* fPts;
877 Quadratic fQ;
878 SkPath::Verb fVerb;
879 double fSide;
880 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000881 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000882 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000883 int fStart;
884 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000885 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000886 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000887};
888
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000889// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000890struct Bounds : public SkRect {
891 static bool Intersects(const Bounds& a, const Bounds& b) {
892 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
893 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
894 }
895
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000896 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
897 if (left < fLeft) {
898 fLeft = left;
899 }
900 if (top < fTop) {
901 fTop = top;
902 }
903 if (right > fRight) {
904 fRight = right;
905 }
906 if (bottom > fBottom) {
907 fBottom = bottom;
908 }
909 }
910
911 void add(const Bounds& toAdd) {
912 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
913 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000914
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000915 void add(const SkPoint& pt) {
916 if (pt.fX < fLeft) fLeft = pt.fX;
917 if (pt.fY < fTop) fTop = pt.fY;
918 if (pt.fX > fRight) fRight = pt.fX;
919 if (pt.fY > fBottom) fBottom = pt.fY;
920 }
921
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000922 bool isEmpty() {
923 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000924 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000925 || isnan(fLeft) || isnan(fRight)
926 || isnan(fTop) || isnan(fBottom);
927 }
928
929 void setCubicBounds(const SkPoint a[4]) {
930 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000931 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000932 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000933 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
934 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000935 }
936
937 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000938 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000939 _Rect dRect;
940 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000941 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
942 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000943 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000944
945 void setPoint(const SkPoint& pt) {
946 fLeft = fRight = pt.fX;
947 fTop = fBottom = pt.fY;
948 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000949};
950
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000951// OPTIMIZATION: does the following also work, and is it any faster?
952// return outerWinding * innerWinding > 0
953// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
caryclark@google.com2ddff932012-08-07 21:25:27 +0000954static bool useInnerWinding(int outerWinding, int innerWinding) {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000955 // SkASSERT(outerWinding != innerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +0000956 int absOut = abs(outerWinding);
957 int absIn = abs(innerWinding);
958 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
959 if (outerWinding * innerWinding < 0) {
960#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000961 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000962 outerWinding, innerWinding, result ? "true" : "false");
963#endif
964 }
965 return result;
966}
967
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000968#define F (false) // discard the edge
969#define T (true) // keep the edge
970
caryclark@google.comd0deb4f2012-12-17 13:58:08 +0000971static const bool gUnaryActiveEdge[2][2] = {
972// from=0 from=1
973// to=0,1 to=0,1
974 {F, T}, {T, F},
975};
976
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000977static const bool gActiveEdge[kShapeOp_Count][2][2][2][2] = {
978// miFrom=0 miFrom=1
979// miTo=0 miTo=1 miTo=0 miTo=1
980// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
981// suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1
982 {{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su
983 {{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su
984 {{{{F, T}, {T, F}}, {{T, T}, {F, F}}}, {{{T, F}, {T, F}}, {{F, F}, {F, F}}}}, // mi | su
985 {{{{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 +0000986};
987
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000988#undef F
989#undef T
caryclark@google.com235f56a2012-09-14 14:19:30 +0000990
caryclark@google.comf839c032012-10-26 21:03:50 +0000991// wrap path to keep track of whether the contour is initialized and non-empty
992class PathWrapper {
993public:
994 PathWrapper(SkPath& path)
995 : fPathPtr(&path)
caryclark@google.comd0deb4f2012-12-17 13:58:08 +0000996 , fCloses(0)
997 , fMoves(0)
caryclark@google.comf839c032012-10-26 21:03:50 +0000998 {
999 init();
1000 }
1001
1002 void close() {
1003 if (!fHasMove) {
1004 return;
1005 }
1006 bool callClose = isClosed();
1007 lineTo();
1008 if (fEmpty) {
1009 return;
1010 }
1011 if (callClose) {
1012 #if DEBUG_PATH_CONSTRUCTION
1013 SkDebugf("path.close();\n");
1014 #endif
1015 fPathPtr->close();
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001016 fCloses++;
caryclark@google.comf839c032012-10-26 21:03:50 +00001017 }
1018 init();
1019 }
1020
1021 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
1022 lineTo();
1023 moveTo();
1024#if DEBUG_PATH_CONSTRUCTION
1025 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
1026 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
1027#endif
1028 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
1029 fDefer[0] = fDefer[1] = pt3;
1030 fEmpty = false;
1031 }
1032
1033 void deferredLine(const SkPoint& pt) {
1034 if (pt == fDefer[1]) {
1035 return;
1036 }
1037 if (changedSlopes(pt)) {
1038 lineTo();
1039 fDefer[0] = fDefer[1];
1040 }
1041 fDefer[1] = pt;
1042 }
1043
1044 void deferredMove(const SkPoint& pt) {
1045 fMoved = true;
1046 fHasMove = true;
1047 fEmpty = true;
1048 fDefer[0] = fDefer[1] = pt;
1049 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001050
caryclark@google.comf839c032012-10-26 21:03:50 +00001051 void deferredMoveLine(const SkPoint& pt) {
1052 if (!fHasMove) {
1053 deferredMove(pt);
1054 }
1055 deferredLine(pt);
1056 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001057
caryclark@google.comf839c032012-10-26 21:03:50 +00001058 bool hasMove() const {
1059 return fHasMove;
1060 }
1061
1062 void init() {
1063 fEmpty = true;
1064 fHasMove = false;
1065 fMoved = false;
1066 }
1067
1068 bool isClosed() const {
1069 return !fEmpty && fFirstPt == fDefer[1];
1070 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001071
caryclark@google.comf839c032012-10-26 21:03:50 +00001072 void lineTo() {
1073 if (fDefer[0] == fDefer[1]) {
1074 return;
1075 }
1076 moveTo();
1077 fEmpty = false;
1078#if DEBUG_PATH_CONSTRUCTION
1079 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
1080#endif
1081 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
1082 fDefer[0] = fDefer[1];
1083 }
1084
1085 const SkPath* nativePath() const {
1086 return fPathPtr;
1087 }
1088
1089 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
1090 lineTo();
1091 moveTo();
1092#if DEBUG_PATH_CONSTRUCTION
1093 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
1094 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1095#endif
1096 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1097 fDefer[0] = fDefer[1] = pt2;
1098 fEmpty = false;
1099 }
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +00001100
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001101 bool someAssemblyRequired() const {
1102 return fCloses < fMoves;
1103 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001104
1105protected:
1106 bool changedSlopes(const SkPoint& pt) const {
1107 if (fDefer[0] == fDefer[1]) {
1108 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001109 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001110 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
1111 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
1112 SkScalar lineDx = pt.fX - fDefer[1].fX;
1113 SkScalar lineDy = pt.fY - fDefer[1].fY;
1114 return deferDx * lineDy != deferDy * lineDx;
1115 }
1116
1117 void moveTo() {
1118 if (!fMoved) {
1119 return;
1120 }
1121 fFirstPt = fDefer[0];
1122#if DEBUG_PATH_CONSTRUCTION
1123 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
1124#endif
1125 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
1126 fMoved = false;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001127 fMoves++;
caryclark@google.comf839c032012-10-26 21:03:50 +00001128 }
1129
1130private:
1131 SkPath* fPathPtr;
1132 SkPoint fDefer[2];
1133 SkPoint fFirstPt;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001134 int fCloses;
1135 int fMoves;
caryclark@google.comf839c032012-10-26 21:03:50 +00001136 bool fEmpty;
1137 bool fHasMove;
1138 bool fMoved;
1139};
1140
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001141class Segment {
1142public:
1143 Segment() {
1144#if DEBUG_DUMP
1145 fID = ++gSegmentID;
1146#endif
1147 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001148
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001149 bool operator<(const Segment& rh) const {
1150 return fBounds.fTop < rh.fBounds.fTop;
1151 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001152
caryclark@google.com4eeda372012-12-06 21:47:48 +00001153 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001154 if (activeAngleInner(index, done, angles)) {
1155 return true;
1156 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001157 int lesser = index;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001158 while (--lesser >= 0 && equalPoints(index, lesser)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001159 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001160 return true;
1161 }
1162 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001163 lesser = index;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001164 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001165 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001166 return true;
1167 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001168 } while (++index < fTs.count() && equalPoints(index, lesser));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001169 return false;
1170 }
1171
caryclark@google.com4eeda372012-12-06 21:47:48 +00001172 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001173 Span* span = &fTs[index];
1174 Segment* other = span->fOther;
1175 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001176 return other->activeAngleInner(oIndex, done, angles);
1177 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001178
caryclark@google.com4eeda372012-12-06 21:47:48 +00001179 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001180 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001181 if (next > 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001182 Span& upSpan = fTs[index];
1183 if (upSpan.fWindValue || upSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001184 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001185 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001186 done++;
1187 } else if (upSpan.fWindSum != SK_MinS32) {
1188 return true;
1189 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001190 } else if (!upSpan.fDone) {
1191 upSpan.fDone = true;
1192 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001193 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001194 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001195 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001196 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001197 if (prev >= 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001198 Span& downSpan = fTs[prev];
1199 if (downSpan.fWindValue || downSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001200 addAngle(angles, index, prev);
1201 if (downSpan.fDone) {
1202 done++;
1203 } else if (downSpan.fWindSum != SK_MinS32) {
1204 return true;
1205 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001206 } else if (!downSpan.fDone) {
1207 downSpan.fDone = true;
1208 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001209 }
1210 }
1211 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001212 }
1213
caryclark@google.comf839c032012-10-26 21:03:50 +00001214 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001215 SkASSERT(!done());
1216 int count = fTs.count();
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001217 result.fX = result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001218 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001219 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001220 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001221 const Span& span = fTs[index];
1222 if (span.fUnsortableStart | lastUnsortable) {
1223 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001224 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001225 if (!span.fDone | !lastDone) {
1226 const SkPoint& xy = xyAtT(index);
1227 if (result.fY < xy.fY) {
1228 goto next;
1229 }
1230 if (result.fY == xy.fY && result.fX < xy.fX) {
1231 goto next;
1232 }
1233 result = xy;
1234 }
1235 next:
1236 lastDone = span.fDone;
1237 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001238 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001239 }
1240
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001241 bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, ShapeOp op) {
1242 int sumMiWinding = updateWinding(endIndex, index);
1243 int sumSuWinding = updateOppWinding(endIndex, index);
1244 if (fOperand) {
1245 SkTSwap<int>(sumMiWinding, sumSuWinding);
1246 }
1247 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
1248 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, sumMiWinding, sumSuWinding,
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001249 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001250 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001251
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001252 bool activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, ShapeOp op,
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001253 int& sumMiWinding, int& sumSuWinding,
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001254 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
1255 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
1256 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001257 bool miFrom;
1258 bool miTo;
1259 bool suFrom;
1260 bool suTo;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001261 if (operand()) {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001262 miFrom = (oppMaxWinding & xorMiMask) != 0;
1263 miTo = (oppSumWinding & xorMiMask) != 0;
1264 suFrom = (maxWinding & xorSuMask) != 0;
1265 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001266 } else {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001267 miFrom = (maxWinding & xorMiMask) != 0;
1268 miTo = (sumWinding & xorMiMask) != 0;
1269 suFrom = (oppMaxWinding & xorSuMask) != 0;
1270 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001271 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001272 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
1273 SkASSERT(result != -1);
1274 return result;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001275 }
1276
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001277 bool activeWinding(int index, int endIndex) {
1278 int sumWinding = updateWinding(endIndex, index);
1279 int maxWinding;
1280 return activeWinding(index, endIndex, maxWinding, sumWinding);
1281 }
1282
1283 bool activeWinding(int index, int endIndex, int& maxWinding, int& sumWinding) {
1284 setUpWinding(index, endIndex, maxWinding, sumWinding);
1285 bool from = maxWinding != 0;
1286 bool to = sumWinding != 0;
1287 bool result = gUnaryActiveEdge[from][to];
1288 SkASSERT(result != -1);
1289 return result;
1290 }
1291
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001292 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001293 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001294 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001295#if DEBUG_ANGLE
caryclark@google.com31143cf2012-11-09 22:14:19 +00001296 if (angles.count() > 1 && !fTs[start].fTiny) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001297 SkPoint angle0Pt, newPt;
1298 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1299 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1300 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
caryclark@google.com6d0032a2013-01-04 19:41:13 +00001301 SkASSERT(AlmostEqualUlps(angle0Pt.fX, newPt.fX));
1302 SkASSERT(AlmostEqualUlps(angle0Pt.fY, newPt.fY));
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001303 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001304#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001305 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001306 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001307
caryclark@google.com2ddff932012-08-07 21:25:27 +00001308 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001309 double oEnd) {
1310 int tIndex = -1;
1311 int tCount = fTs.count();
1312 int oIndex = -1;
1313 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001314 do {
1315 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001316 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001317 int tIndexStart = tIndex;
1318 do {
1319 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001320 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001321 int oIndexStart = oIndex;
1322 double nextT;
1323 do {
1324 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001325 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001326 double oNextT;
1327 do {
1328 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001329 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001330 // at this point, spans before and after are at:
1331 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1332 // if tIndexStart == 0, no prior span
1333 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001334
caryclark@google.comcc905052012-07-25 20:59:42 +00001335 // advance the span with zero winding
1336 // if the following span exists (not past the end, non-zero winding)
1337 // connect the two edges
1338 if (!fTs[tIndexStart].fWindValue) {
1339 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1340 #if DEBUG_CONCIDENT
1341 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1342 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001343 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1344 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001345 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001346 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001347 }
1348 if (nextT < 1 && fTs[tIndex].fWindValue) {
1349 #if DEBUG_CONCIDENT
1350 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1351 __FUNCTION__, fID, other.fID, tIndex,
1352 fTs[tIndex].fT, xyAtT(tIndex).fX,
1353 xyAtT(tIndex).fY);
1354 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001355 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001356 }
1357 } else {
1358 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1359 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1360 #if DEBUG_CONCIDENT
1361 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1362 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001363 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1364 other.xyAtT(oIndexStart).fY);
1365 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001366 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001367 }
1368 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1369 #if DEBUG_CONCIDENT
1370 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1371 __FUNCTION__, fID, other.fID, oIndex,
1372 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1373 other.xyAtT(oIndex).fY);
1374 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1375 #endif
1376 }
1377 }
1378 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001379
caryclark@google.comcc905052012-07-25 20:59:42 +00001380 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1381 double oEnd) {
1382 // walk this to outsideTs[0]
1383 // walk other to outsideTs[1]
1384 // if either is > 0, add a pointer to the other, copying adjacent winding
1385 int tIndex = -1;
1386 int oIndex = -1;
1387 double tStart = outsideTs[0];
1388 double oStart = outsideTs[1];
1389 do {
1390 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001391 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001392 do {
1393 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001394 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001395 if (tIndex > 0 || oIndex > 0 || fOperand != other.fOperand) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001396 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001397 }
1398 tStart = fTs[tIndex].fT;
1399 oStart = other.fTs[oIndex].fT;
1400 do {
1401 double nextT;
1402 do {
1403 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001404 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001405 tStart = nextT;
1406 do {
1407 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001408 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001409 oStart = nextT;
caryclark@google.com4eeda372012-12-06 21:47:48 +00001410 if (tStart == 1 && oStart == 1 && fOperand == other.fOperand) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001411 break;
1412 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001413 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001414 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001415 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001416
caryclark@google.com4eeda372012-12-06 21:47:48 +00001417 void addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
1418 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001419 fBounds.setCubicBounds(pts);
1420 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001421
caryclark@google.comf839c032012-10-26 21:03:50 +00001422 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001423 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001424 const SkPoint* ePtr;
1425 int lastT = fTs.count() - 1;
1426 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1427 ePtr = fPts;
1428 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001429 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001430 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1431 ePtr = edge;
1432 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001433 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001434 bool reverse = ePtr == fPts && start != 0;
1435 if (reverse) {
1436 path.deferredMoveLine(ePtr[fVerb]);
1437 switch (fVerb) {
1438 case SkPath::kLine_Verb:
1439 path.deferredLine(ePtr[0]);
1440 break;
1441 case SkPath::kQuad_Verb:
1442 path.quadTo(ePtr[1], ePtr[0]);
1443 break;
1444 case SkPath::kCubic_Verb:
1445 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1446 break;
1447 default:
1448 SkASSERT(0);
1449 }
1450 // return ePtr[0];
1451 } else {
1452 path.deferredMoveLine(ePtr[0]);
1453 switch (fVerb) {
1454 case SkPath::kLine_Verb:
1455 path.deferredLine(ePtr[1]);
1456 break;
1457 case SkPath::kQuad_Verb:
1458 path.quadTo(ePtr[1], ePtr[2]);
1459 break;
1460 case SkPath::kCubic_Verb:
1461 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1462 break;
1463 default:
1464 SkASSERT(0);
1465 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001466 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001467 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001468 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001469 }
1470
caryclark@google.com4eeda372012-12-06 21:47:48 +00001471 void addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
1472 init(pts, SkPath::kLine_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001473 fBounds.set(pts, 2);
1474 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001475
caryclark@google.comf839c032012-10-26 21:03:50 +00001476#if 0
1477 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001478 const SkPoint& pt = xyAtT(tIndex);
1479 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001480 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001481 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001482 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001483 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001484#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001485
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001486 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001487 void addOtherT(int index, double otherT, int otherIndex) {
1488 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001489 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001490 if (precisely_less_than_zero(otherT)) {
1491 otherT = 0;
1492 } else if (precisely_greater_than_one(otherT)) {
1493 otherT = 1;
1494 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001495 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001496 span.fOtherT = otherT;
1497 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001498 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001499
caryclark@google.com4eeda372012-12-06 21:47:48 +00001500 void addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
1501 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001502 fBounds.setQuadBounds(pts);
1503 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001504
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001505 // Defer all coincident edge processing until
1506 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001507
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001508// no need to be tricky; insert in normal T order
1509// resolve overlapping ts when considering coincidence later
1510
1511 // add non-coincident intersection. Resulting edges are sorted in T.
1512 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001513 // FIXME: in the pathological case where there is a ton of intercepts,
1514 // binary search?
1515 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001516 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001517 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001518 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001519 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001520 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001521 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001522 newT = 1;
1523 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001524 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001525 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001526 // OPTIMIZATION: if there are three or more identical Ts, then
1527 // the fourth and following could be further insertion-sorted so
1528 // that all the edges are clockwise or counterclockwise.
1529 // This could later limit segment tests to the two adjacent
1530 // neighbors, although it doesn't help with determining which
1531 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001532 if (newT < fTs[index].fT) {
1533 insertedAt = index;
1534 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001535 }
1536 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001537 Span* span;
1538 if (insertedAt >= 0) {
1539 span = fTs.insert(insertedAt);
1540 } else {
1541 insertedAt = tCount;
1542 span = fTs.append();
1543 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001544 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001545 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001546 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001547 span->fWindSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001548 span->fOppSum = SK_MinS32;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001549 span->fWindValue = 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001550 span->fOppValue = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001551 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001552 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001553 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001554 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001555 span->fUnsortableStart = false;
1556 span->fUnsortableEnd = false;
caryclark@google.com8f9f4682013-01-03 21:18:16 +00001557 int less = -1;
1558 while (&span[less + 1] - fTs.begin() > 0 && !span[less].fDone
1559 && !precisely_negative(newT - span[less].fT)
1560 // && approximately_negative(newT - span[less].fT)
1561 && xyAtT(&span[less]) == xyAtT(span)) {
1562 span[less].fTiny = true;
1563 span[less].fDone = true;
1564 if (approximately_negative(newT - span[less].fT)) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00001565 if (approximately_greater_than_one(newT)) {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00001566 span[less].fUnsortableStart = true;
1567 span[less - 1].fUnsortableEnd = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001568 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +00001569 if (approximately_less_than_zero(span[less].fT)) {
1570 span[less + 1].fUnsortableStart = true;
1571 span[less].fUnsortableEnd = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001572 }
1573 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001574 ++fDoneSpans;
caryclark@google.com8f9f4682013-01-03 21:18:16 +00001575 --less;
caryclark@google.comf839c032012-10-26 21:03:50 +00001576 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +00001577 int more = 1;
1578 while (fTs.end() - &span[more - 1] > 1 && !span[more - 1].fDone
1579 && !precisely_negative(span[more].fT - newT)
1580 // && approximately_negative(span[more].fT - newT)
1581 && xyAtT(&span[more]) == xyAtT(span)) {
1582 span[more - 1].fTiny = true;
1583 span[more - 1].fDone = true;
1584 if (approximately_negative(span[more].fT - newT)) {
1585 if (approximately_greater_than_one(span[more].fT)) {
1586 span[more + 1].fUnsortableStart = true;
1587 span[more].fUnsortableEnd = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001588 }
1589 if (approximately_less_than_zero(newT)) {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00001590 span[more].fUnsortableStart = true;
1591 span[more - 1].fUnsortableEnd = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001592 }
1593 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001594 ++fDoneSpans;
caryclark@google.com8f9f4682013-01-03 21:18:16 +00001595 ++more;
caryclark@google.comf839c032012-10-26 21:03:50 +00001596 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001597 return insertedAt;
1598 }
1599
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001600 // set spans from start to end to decrement by one
1601 // note this walks other backwards
1602 // FIMXE: there's probably an edge case that can be constructed where
1603 // two span in one segment are separated by float epsilon on one span but
1604 // not the other, if one segment is very small. For this
1605 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001606 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001607 // sorted? It feels better in such a case to match the span's other span
1608 // pointer since both coincident segments must contain the same spans.
1609 void addTCancel(double startT, double endT, Segment& other,
1610 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001611 SkASSERT(!approximately_negative(endT - startT));
1612 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001613 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001614 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001615 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001616 ++index;
1617 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001618 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001619 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001620 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001621 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001622 Span* test = &fTs[index];
1623 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001624 SkTDArray<double> outsideTs;
1625 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001626 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001627 bool decrement = test->fWindValue && oTest->fWindValue && !binary;
caryclark@google.comcc905052012-07-25 20:59:42 +00001628 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001629 double testT = test->fT;
1630 double oTestT = oTest->fT;
1631 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001632 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001633 if (decrement) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001634 decrementSpan(span);
caryclark@google.com200c2112012-08-03 15:05:04 +00001635 } else if (track && span->fT < 1 && oTestT < 1) {
1636 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001637 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001638 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001639 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001640 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001641 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1642 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1643 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001644 while (approximately_negative(otherTMatchStart - oSpan->fT)
1645 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001646 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001647 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001648 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001649 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001650 other.decrementSpan(oSpan);
1651 } else if (track && oSpan->fT < 1 && testT < 1) {
1652 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001653 }
1654 if (!oIndex) {
1655 break;
1656 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001657 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001658 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001659 test = span;
1660 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001661 } while (!approximately_negative(endT - test->fT));
1662 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001663 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001664 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001665 double tStart = outsideTs[0];
1666 double oStart = outsideTs[1];
1667 addCancelOutsides(tStart, oStart, other, oEndT);
1668 int count = outsideTs.count();
1669 if (count > 2) {
1670 double tStart = outsideTs[count - 2];
1671 double oStart = outsideTs[count - 1];
1672 addCancelOutsides(tStart, oStart, other, oEndT);
1673 }
caryclark@google.com18063442012-07-25 12:05:18 +00001674 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001675 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001676 double tStart = oOutsideTs[0];
1677 double oStart = oOutsideTs[1];
1678 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001679 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001680 }
caryclark@google.com73ca6242013-01-17 21:02:47 +00001681
1682 int addUnsortableT(double newT, Segment* other, bool start) {
1683 int result = addT(newT, other);
1684 Span* span = &fTs[result];
1685 if (start) {
1686 if (result > 0) {
1687 span[result - 1].fUnsortableEnd = true;
1688 }
1689 span[result].fUnsortableStart = true;
1690 } else {
1691 span[result].fUnsortableEnd = true;
1692 if (result + 1 < fTs.count()) {
1693 span[result + 1].fUnsortableStart = true;
1694 }
1695 }
1696 return result;
1697 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00001698
caryclark@google.com4eeda372012-12-06 21:47:48 +00001699 int bumpCoincidentThis(const Span* oTest, bool opp, int index,
1700 SkTDArray<double>& outsideTs) {
1701 int oWindValue = oTest->fWindValue;
1702 int oOppValue = oTest->fOppValue;
1703 if (opp) {
1704 SkTSwap<int>(oWindValue, oOppValue);
1705 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001706 Span* const test = &fTs[index];
1707 Span* end = test;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001708 const double oStartT = oTest->fT;
1709 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001710 if (bumpSpan(end, oWindValue, oOppValue)) {
1711 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001712 }
1713 end = &fTs[++index];
1714 } while (approximately_negative(end->fT - test->fT));
1715 return index;
1716 }
1717
1718 // because of the order in which coincidences are resolved, this and other
1719 // may not have the same intermediate points. Compute the corresponding
1720 // intermediate T values (using this as the master, other as the follower)
1721 // and walk other conditionally -- hoping that it catches up in the end
caryclark@google.com4eeda372012-12-06 21:47:48 +00001722 int bumpCoincidentOther(const Span* test, double oEndT, int& oIndex,
1723 SkTDArray<double>& oOutsideTs) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001724 Span* const oTest = &fTs[oIndex];
1725 Span* oEnd = oTest;
1726 const double startT = test->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001727 const double oStartT = oTest->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001728 while (!approximately_negative(oEndT - oEnd->fT)
caryclark@google.com4eeda372012-12-06 21:47:48 +00001729 && approximately_negative(oEnd->fT - oStartT)) {
1730 zeroSpan(oEnd);
1731 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001732 oEnd = &fTs[++oIndex];
1733 }
1734 return oIndex;
1735 }
1736
1737 // FIXME: need to test this case:
1738 // contourA has two segments that are coincident
1739 // contourB has two segments that are coincident in the same place
1740 // each ends up with +2/0 pairs for winding count
1741 // since logic below doesn't transfer count (only increments/decrements) can this be
1742 // resolved to +4/0 ?
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001743
1744 // set spans from start to end to increment the greater by one and decrement
1745 // the lesser
caryclark@google.com4eeda372012-12-06 21:47:48 +00001746 void addTCoincident(double startT, double endT, Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001747 SkASSERT(!approximately_negative(endT - startT));
1748 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001749 bool opp = fOperand ^ other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001750 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001751 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001752 ++index;
1753 }
1754 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001755 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001756 ++oIndex;
1757 }
1758 Span* test = &fTs[index];
1759 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001760 SkTDArray<double> outsideTs;
1761 SkTDArray<double> oOutsideTs;
1762 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001763 // if either span has an opposite value and the operands don't match, resolve first
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001764 // SkASSERT(!test->fDone || !oTest->fDone);
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001765 if (test->fDone || oTest->fDone) {
1766 index = advanceCoincidentThis(oTest, opp, index);
1767 oIndex = other.advanceCoincidentOther(test, oEndT, oIndex);
1768 } else {
1769 index = bumpCoincidentThis(oTest, opp, index, outsideTs);
1770 oIndex = other.bumpCoincidentOther(test, oEndT, oIndex, oOutsideTs);
1771 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001772 test = &fTs[index];
1773 oTest = &other.fTs[oIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001774 } while (!approximately_negative(endT - test->fT));
1775 SkASSERT(approximately_negative(oTest->fT - oEndT));
1776 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001777 if (!done() && outsideTs.count()) {
1778 addCoinOutsides(outsideTs, other, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001779 }
1780 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001781 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001782 }
1783 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001784
caryclark@google.comcc905052012-07-25 20:59:42 +00001785 // FIXME: this doesn't prevent the same span from being added twice
1786 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001787 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001788 int tCount = fTs.count();
1789 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1790 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001791 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001792 break;
1793 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001794 if (approximately_negative(span.fT - t) && span.fOther == &other
1795 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001796#if DEBUG_ADD_T_PAIR
1797 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1798 __FUNCTION__, fID, t, other.fID, otherT);
1799#endif
1800 return;
1801 }
1802 }
caryclark@google.com47580692012-07-23 12:14:49 +00001803#if DEBUG_ADD_T_PAIR
1804 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1805 __FUNCTION__, fID, t, other.fID, otherT);
1806#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001807 int insertedAt = addT(t, &other);
1808 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001809 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001810 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001811 matchWindingValue(insertedAt, t, borrowWind);
1812 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001813 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001814
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001815 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001816 // add edge leading into junction
caryclark@google.com4eeda372012-12-06 21:47:48 +00001817 int min = SkMin32(end, start);
1818 if (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001819 addAngle(angles, end, start);
1820 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001821 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001822 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001823 int tIndex = nextExactSpan(end, step);
caryclark@google.com4eeda372012-12-06 21:47:48 +00001824 min = SkMin32(end, tIndex);
1825 if (tIndex >= 0 && (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001826 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001827 }
1828 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001829
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001830 int advanceCoincidentThis(const Span* oTest, bool opp, int index) {
1831 Span* const test = &fTs[index];
1832 Span* end = test;
1833 do {
1834 end = &fTs[++index];
1835 } while (approximately_negative(end->fT - test->fT));
1836 return index;
1837 }
1838
1839 int advanceCoincidentOther(const Span* test, double oEndT, int& oIndex) {
1840 Span* const oTest = &fTs[oIndex];
1841 Span* oEnd = oTest;
1842 const double oStartT = oTest->fT;
1843 while (!approximately_negative(oEndT - oEnd->fT)
1844 && approximately_negative(oEnd->fT - oStartT)) {
1845 oEnd = &fTs[++oIndex];
1846 }
1847 return oIndex;
1848 }
skia.committer@gmail.comb89a03c2012-12-22 02:02:33 +00001849
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001850 bool betweenTs(int lesser, double testT, int greater) {
1851 if (lesser > greater) {
1852 SkTSwap<int>(lesser, greater);
1853 }
1854 return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
1855 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001856
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001857 const Bounds& bounds() const {
1858 return fBounds;
1859 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001860
caryclark@google.com31143cf2012-11-09 22:14:19 +00001861 void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001862 double referenceT = fTs[index].fT;
1863 int lesser = index;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001864 while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand)
1865 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00001866 buildAnglesInner(lesser, angles);
1867 }
1868 do {
1869 buildAnglesInner(index, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001870 } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand)
1871 && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001872 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001873
1874 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1875 Span* span = &fTs[index];
1876 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001877 // if there is only one live crossing, and no coincidence, continue
1878 // in the same direction
1879 // if there is coincidence, the only choice may be to reverse direction
1880 // find edge on either side of intersection
1881 int oIndex = span->fOtherIndex;
1882 // if done == -1, prior span has already been processed
1883 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001884 int next = other->nextExactSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001885 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001886 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001887 next = other->nextExactSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001888 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001889 // add candidate into and away from junction
1890 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001891 }
1892
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001893 int computeSum(int startIndex, int endIndex, bool binary) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001894 SkTDArray<Angle> angles;
1895 addTwoAngles(startIndex, endIndex, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001896 buildAngles(endIndex, angles, false);
caryclark@google.comd1688742012-09-18 20:08:37 +00001897 // OPTIMIZATION: check all angles to see if any have computed wind sum
1898 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001899 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001900 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001901#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001902 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001903#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001904 if (!sortable) {
1905 return SK_MinS32;
1906 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001907 int angleCount = angles.count();
1908 const Angle* angle;
1909 const Segment* base;
1910 int winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001911 int oWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001912 int firstIndex = 0;
1913 do {
1914 angle = sorted[firstIndex];
1915 base = angle->segment();
1916 winding = base->windSum(angle);
1917 if (winding != SK_MinS32) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001918 oWinding = base->oppSum(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001919 break;
1920 }
1921 if (++firstIndex == angleCount) {
1922 return SK_MinS32;
1923 }
1924 } while (true);
1925 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001926 int spanWinding = base->spanSign(angle);
1927 bool inner = useInnerWinding(winding + spanWinding, winding);
1928 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001929 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001930 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001931 inner ? winding + spanWinding : winding);
1932 #endif
1933 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001934 winding += spanWinding;
1935 }
1936 #if DEBUG_SORT
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001937 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001938 #endif
1939 int nextIndex = firstIndex + 1;
1940 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001941 winding -= base->spanSign(angle);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001942 oWinding -= base->oppSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001943 do {
1944 if (nextIndex == angleCount) {
1945 nextIndex = 0;
1946 }
1947 angle = sorted[nextIndex];
1948 Segment* segment = angle->segment();
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001949 bool opp = base->fOperand ^ segment->fOperand;
1950 int maxWinding, oMaxWinding;
1951 int spanSign = segment->spanSign(angle);
1952 int oppoSign = segment->oppSign(angle);
1953 if (opp) {
1954 oMaxWinding = oWinding;
1955 oWinding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001956 maxWinding = winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001957 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001958 winding -= oppoSign;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001959 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001960 } else {
1961 maxWinding = winding;
1962 winding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001963 oMaxWinding = oWinding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001964 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001965 oWinding -= oppoSign;
1966 }
1967 }
1968 if (segment->windSum(angle) == SK_MinS32) {
1969 if (opp) {
1970 if (useInnerWinding(oMaxWinding, oWinding)) {
1971 oMaxWinding = oWinding;
1972 }
1973 if (oppoSign && useInnerWinding(maxWinding, winding)) {
1974 maxWinding = winding;
1975 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001976 (void) segment->markAndChaseWinding(angle, oMaxWinding, maxWinding);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001977 } else {
1978 if (useInnerWinding(maxWinding, winding)) {
1979 maxWinding = winding;
1980 }
1981 if (oppoSign && useInnerWinding(oMaxWinding, oWinding)) {
1982 oMaxWinding = oWinding;
1983 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001984 (void) segment->markAndChaseWinding(angle, maxWinding,
1985 binary ? oMaxWinding : 0);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001986 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001987 }
1988 } while (++nextIndex != lastIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001989 int minIndex = SkMin32(startIndex, endIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001990 return windSum(minIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001991 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001992
caryclark@google.com3586ece2012-12-27 18:46:58 +00001993 int crossedSpanY(const SkPoint& basePt, SkScalar& bestY, double& hitT, bool& hitSomething,
caryclark@google.com10227bf2012-12-28 22:10:41 +00001994 double mid, bool opp, bool current) const {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001995 SkScalar bottom = fBounds.fBottom;
caryclark@google.com10227bf2012-12-28 22:10:41 +00001996 int bestTIndex = -1;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001997 if (bottom <= bestY) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00001998 return bestTIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001999 }
2000 SkScalar top = fBounds.fTop;
2001 if (top >= basePt.fY) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00002002 return bestTIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002003 }
2004 if (fBounds.fLeft > basePt.fX) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00002005 return bestTIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002006 }
2007 if (fBounds.fRight < basePt.fX) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00002008 return bestTIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002009 }
2010 if (fBounds.fLeft == fBounds.fRight) {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002011 // if vertical, and directly above test point, wait for another one
caryclark@google.com6d0032a2013-01-04 19:41:13 +00002012 return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002013 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00002014 // intersect ray starting at basePt with edge
2015 Intersections intersections;
2016 // OPTIMIZE: use specialty function that intersects ray with curve,
2017 // returning t values only for curve (we don't care about t on ray)
2018 int pts = (*VSegmentIntersect[fVerb])(fPts, top, bottom, basePt.fX, false, intersections);
2019 if (pts == 0 || (current && pts == 1)) {
2020 return bestTIndex;
2021 }
2022 if (current) {
2023 SkASSERT(pts > 1);
2024 int closestIdx = 0;
2025 double closest = fabs(intersections.fT[0][0] - mid);
2026 for (int idx = 1; idx < pts; ++idx) {
2027 double test = fabs(intersections.fT[0][idx] - mid);
2028 if (closest > test) {
2029 closestIdx = idx;
2030 closest = test;
2031 }
2032 }
2033 if (closestIdx < pts - 1) {
2034 intersections.fT[0][closestIdx] = intersections.fT[0][pts - 1];
2035 }
2036 --pts;
2037 }
2038 double bestT = -1;
2039 for (int index = 0; index < pts; ++index) {
2040 double foundT = intersections.fT[0][index];
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002041 if (approximately_less_than_zero(foundT)
2042 || approximately_greater_than_one(foundT)) {
2043 continue;
2044 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00002045 SkScalar testY = (*SegmentYAtT[fVerb])(fPts, foundT);
2046 if (approximately_negative(testY - bestY)
2047 || approximately_negative(basePt.fY - testY)) {
caryclark@google.com47580692012-07-23 12:14:49 +00002048 continue;
2049 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00002050 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002051 return SK_MinS32; // if the intersection is edge on, wait for another one
caryclark@google.com10227bf2012-12-28 22:10:41 +00002052 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002053 if (fVerb > SkPath::kLine_Verb) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00002054 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, foundT);
2055 if (approximately_zero(dx)) {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002056 return SK_MinS32; // hit vertical, wait for another one
caryclark@google.com3586ece2012-12-27 18:46:58 +00002057 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002058 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00002059 bestY = testY;
2060 bestT = foundT;
caryclark@google.com3586ece2012-12-27 18:46:58 +00002061 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00002062 if (bestT < 0) {
2063 return bestTIndex;
caryclark@google.com3586ece2012-12-27 18:46:58 +00002064 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00002065 SkASSERT(bestT >= 0);
2066 SkASSERT(bestT <= 1);
2067 int start;
2068 int end = 0;
2069 do {
2070 start = end;
2071 end = nextSpan(start, 1);
2072 } while (fTs[end].fT < bestT);
2073 // FIXME: see next candidate for a better pattern to find the next start/end pair
2074 while (start + 1 < end && fTs[start].fDone) {
2075 ++start;
caryclark@google.com3586ece2012-12-27 18:46:58 +00002076 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002077 if (!isCanceled(start)) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00002078 hitT = bestT;
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002079 bestTIndex = start;
caryclark@google.com10227bf2012-12-28 22:10:41 +00002080 hitSomething = true;
caryclark@google.com3586ece2012-12-27 18:46:58 +00002081 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00002082 return bestTIndex;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002083 }
caryclark@google.com18063442012-07-25 12:05:18 +00002084
caryclark@google.com4eeda372012-12-06 21:47:48 +00002085 void decrementSpan(Span* span) {
caryclark@google.com18063442012-07-25 12:05:18 +00002086 SkASSERT(span->fWindValue > 0);
2087 if (--(span->fWindValue) == 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00002088 if (!span->fOppValue && !span->fDone) {
caryclark@google.comf839c032012-10-26 21:03:50 +00002089 span->fDone = true;
2090 ++fDoneSpans;
2091 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002092 }
2093 }
2094
2095 bool bumpSpan(Span* span, int windDelta, int oppDelta) {
2096 SkASSERT(!span->fDone);
2097 span->fWindValue += windDelta;
2098 SkASSERT(span->fWindValue >= 0);
2099 span->fOppValue += oppDelta;
2100 SkASSERT(span->fOppValue >= 0);
2101 if (fXor) {
2102 span->fWindValue &= 1;
2103 }
2104 if (fOppXor) {
2105 span->fOppValue &= 1;
2106 }
2107 if (!span->fWindValue && !span->fOppValue) {
2108 span->fDone = true;
2109 ++fDoneSpans;
caryclark@google.com18063442012-07-25 12:05:18 +00002110 return true;
2111 }
2112 return false;
2113 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002114
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00002115 // OPTIMIZE
2116 // when the edges are initially walked, they don't automatically get the prior and next
2117 // 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 +00002118 // and would additionally remove the need for similar checks in condition edges. It would
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00002119 // also allow intersection code to assume end of segment intersections (maybe?)
2120 bool complete() const {
2121 int count = fTs.count();
2122 return count > 1 && fTs[0].fT == 0 && fTs[--count].fT == 1;
2123 }
caryclark@google.com18063442012-07-25 12:05:18 +00002124
caryclark@google.com15fa1382012-05-07 20:49:36 +00002125 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002126 SkASSERT(fDoneSpans <= fTs.count());
2127 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002128 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002129
caryclark@google.comf839c032012-10-26 21:03:50 +00002130 bool done(int min) const {
2131 return fTs[min].fDone;
2132 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002133
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002134 bool done(const Angle* angle) const {
2135 return done(SkMin32(angle->start(), angle->end()));
caryclark@google.com47580692012-07-23 12:14:49 +00002136 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002137
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002138 bool equalPoints(int greaterTIndex, int lesserTIndex) {
2139 SkASSERT(greaterTIndex >= lesserTIndex);
2140 double greaterT = fTs[greaterTIndex].fT;
2141 double lesserT = fTs[lesserTIndex].fT;
2142 if (greaterT == lesserT) {
2143 return true;
2144 }
2145 if (!approximately_negative(greaterT - lesserT)) {
2146 return false;
2147 }
2148 return xyAtT(greaterTIndex) == xyAtT(lesserTIndex);
2149 }
2150
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002151 /*
2152 The M and S variable name parts stand for the operators.
2153 Mi stands for Minuend (see wiki subtraction, analogous to difference)
2154 Su stands for Subtrahend
2155 The Opp variable name part designates that the value is for the Opposite operator.
2156 Opposite values result from combining coincident spans.
2157 */
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002158
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002159 Segment* findNextOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
2160 bool& unsortable, ShapeOp op, const int xorMiMask, const int xorSuMask) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002161 const int startIndex = nextStart;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002162 const int endIndex = nextEnd;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002163 SkASSERT(startIndex != endIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002164 const int count = fTs.count();
2165 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
2166 const int step = SkSign32(endIndex - startIndex);
2167 const int end = nextExactSpan(startIndex, step);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002168 SkASSERT(end >= 0);
2169 Span* endSpan = &fTs[end];
2170 Segment* other;
2171 if (isSimple(end)) {
2172 // mark the smaller of startIndex, endIndex done, and all adjacent
2173 // spans with the same T value (but not 'other' spans)
2174 #if DEBUG_WINDING
2175 SkDebugf("%s simple\n", __FUNCTION__);
2176 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002177 int min = SkMin32(startIndex, endIndex);
2178 if (fTs[min].fDone) {
2179 return NULL;
2180 }
2181 markDoneBinary(min);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002182 other = endSpan->fOther;
2183 nextStart = endSpan->fOtherIndex;
2184 double startT = other->fTs[nextStart].fT;
2185 nextEnd = nextStart;
2186 do {
2187 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002188 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002189 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002190 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2191 return other;
2192 }
2193 // more than one viable candidate -- measure angles to find best
2194 SkTDArray<Angle> angles;
2195 SkASSERT(startIndex - endIndex != 0);
2196 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2197 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002198 buildAngles(end, angles, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002199 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002200 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002201 int angleCount = angles.count();
2202 int firstIndex = findStartingEdge(sorted, startIndex, end);
2203 SkASSERT(firstIndex >= 0);
2204 #if DEBUG_SORT
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002205 debugShowSort(__FUNCTION__, sorted, firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002206 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002207 if (!sortable) {
2208 unsortable = true;
2209 return NULL;
2210 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002211 SkASSERT(sorted[firstIndex]->segment() == this);
2212 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002213 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
2214 sorted[firstIndex]->sign());
caryclark@google.com235f56a2012-09-14 14:19:30 +00002215 #endif
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002216 int sumMiWinding = updateWinding(endIndex, startIndex);
2217 int sumSuWinding = updateOppWinding(endIndex, startIndex);
2218 if (operand()) {
2219 SkTSwap<int>(sumMiWinding, sumSuWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002220 }
2221 int nextIndex = firstIndex + 1;
2222 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2223 const Angle* foundAngle = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002224 bool foundDone = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002225 // iterate through the angle, and compute everyone's winding
caryclark@google.com235f56a2012-09-14 14:19:30 +00002226 Segment* nextSegment;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002227 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002228 SkASSERT(nextIndex != firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002229 if (nextIndex == angleCount) {
2230 nextIndex = 0;
2231 }
2232 const Angle* nextAngle = sorted[nextIndex];
2233 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002234 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
2235 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
2236 nextAngle->end(), op, sumMiWinding, sumSuWinding,
2237 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
2238 if (activeAngle && (!foundAngle || foundDone)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002239 foundAngle = nextAngle;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002240 foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002241 }
2242 if (nextSegment->done()) {
2243 continue;
2244 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002245 if (nextSegment->windSum(nextAngle) != SK_MinS32) {
2246 continue;
2247 }
2248 Span* last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding,
2249 oppSumWinding, activeAngle, nextAngle);
2250 if (last) {
2251 *chase.append() = last;
2252#if DEBUG_WINDING
2253 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2254 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2255#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002256 }
2257 } while (++nextIndex != lastIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002258 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002259 if (!foundAngle) {
2260 return NULL;
2261 }
2262 nextStart = foundAngle->start();
2263 nextEnd = foundAngle->end();
2264 nextSegment = foundAngle->segment();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002265
caryclark@google.com235f56a2012-09-14 14:19:30 +00002266 #if DEBUG_WINDING
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002267 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2268 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002269 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002270 return nextSegment;
2271 }
caryclark@google.com47580692012-07-23 12:14:49 +00002272
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002273 Segment* findNextWinding(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
2274 bool& unsortable) {
2275 const int startIndex = nextStart;
2276 const int endIndex = nextEnd;
2277 SkASSERT(startIndex != endIndex);
2278 const int count = fTs.count();
2279 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
2280 const int step = SkSign32(endIndex - startIndex);
2281 const int end = nextExactSpan(startIndex, step);
2282 SkASSERT(end >= 0);
2283 Span* endSpan = &fTs[end];
2284 Segment* other;
2285 if (isSimple(end)) {
2286 // mark the smaller of startIndex, endIndex done, and all adjacent
2287 // spans with the same T value (but not 'other' spans)
2288 #if DEBUG_WINDING
2289 SkDebugf("%s simple\n", __FUNCTION__);
2290 #endif
2291 int min = SkMin32(startIndex, endIndex);
2292 if (fTs[min].fDone) {
2293 return NULL;
2294 }
2295 markDoneUnary(min);
2296 other = endSpan->fOther;
2297 nextStart = endSpan->fOtherIndex;
2298 double startT = other->fTs[nextStart].fT;
2299 nextEnd = nextStart;
2300 do {
2301 nextEnd += step;
2302 }
2303 while (precisely_zero(startT - other->fTs[nextEnd].fT));
2304 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2305 return other;
2306 }
2307 // more than one viable candidate -- measure angles to find best
2308 SkTDArray<Angle> angles;
2309 SkASSERT(startIndex - endIndex != 0);
2310 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2311 addTwoAngles(startIndex, end, angles);
2312 buildAngles(end, angles, true);
2313 SkTDArray<Angle*> sorted;
2314 bool sortable = SortAngles(angles, sorted);
2315 int angleCount = angles.count();
2316 int firstIndex = findStartingEdge(sorted, startIndex, end);
2317 SkASSERT(firstIndex >= 0);
2318 #if DEBUG_SORT
2319 debugShowSort(__FUNCTION__, sorted, firstIndex);
2320 #endif
2321 if (!sortable) {
2322 unsortable = true;
2323 return NULL;
2324 }
2325 SkASSERT(sorted[firstIndex]->segment() == this);
2326 #if DEBUG_WINDING
2327 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
2328 sorted[firstIndex]->sign());
2329 #endif
2330 int sumWinding = updateWinding(endIndex, startIndex);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002331 int nextIndex = firstIndex + 1;
2332 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2333 const Angle* foundAngle = NULL;
2334 bool foundDone = false;
2335 // iterate through the angle, and compute everyone's winding
2336 Segment* nextSegment;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002337 int activeCount = 0;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002338 do {
2339 SkASSERT(nextIndex != firstIndex);
2340 if (nextIndex == angleCount) {
2341 nextIndex = 0;
2342 }
2343 const Angle* nextAngle = sorted[nextIndex];
2344 nextSegment = nextAngle->segment();
2345 int maxWinding;
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +00002346 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002347 maxWinding, sumWinding);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002348 if (activeAngle) {
2349 ++activeCount;
2350 if (!foundAngle || (foundDone && activeCount & 1)) {
2351 if (nextSegment->tiny(nextAngle)) {
2352 unsortable = true;
2353 return NULL;
2354 }
2355 foundAngle = nextAngle;
2356 foundDone = nextSegment->done(nextAngle);
2357 }
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002358 }
2359 if (nextSegment->done()) {
2360 continue;
2361 }
2362 if (nextSegment->windSum(nextAngle) != SK_MinS32) {
2363 continue;
2364 }
2365 Span* last = nextSegment->markAngle(maxWinding, sumWinding, activeAngle, nextAngle);
2366 if (last) {
2367 *chase.append() = last;
2368#if DEBUG_WINDING
2369 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2370 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2371#endif
2372 }
2373 } while (++nextIndex != lastIndex);
2374 markDoneUnary(SkMin32(startIndex, endIndex));
2375 if (!foundAngle) {
2376 return NULL;
2377 }
2378 nextStart = foundAngle->start();
2379 nextEnd = foundAngle->end();
2380 nextSegment = foundAngle->segment();
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002381 #if DEBUG_WINDING
2382 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2383 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
2384 #endif
2385 return nextSegment;
2386 }
2387
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002388 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002389 const int startIndex = nextStart;
2390 const int endIndex = nextEnd;
2391 SkASSERT(startIndex != endIndex);
2392 int count = fTs.count();
2393 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2394 : startIndex > 0);
2395 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002396 int end = nextExactSpan(startIndex, step);
caryclark@google.com24bec792012-08-20 12:43:57 +00002397 SkASSERT(end >= 0);
2398 Span* endSpan = &fTs[end];
2399 Segment* other;
caryclark@google.com24bec792012-08-20 12:43:57 +00002400 if (isSimple(end)) {
2401 #if DEBUG_WINDING
2402 SkDebugf("%s simple\n", __FUNCTION__);
2403 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002404 int min = SkMin32(startIndex, endIndex);
2405 if (fTs[min].fDone) {
2406 return NULL;
2407 }
2408 markDone(min, 1);
caryclark@google.com24bec792012-08-20 12:43:57 +00002409 other = endSpan->fOther;
2410 nextStart = endSpan->fOtherIndex;
2411 double startT = other->fTs[nextStart].fT;
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002412 #if 01 // FIXME: I don't know why the logic here is difference from the winding case
caryclark@google.com24bec792012-08-20 12:43:57 +00002413 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002414 if ((approximately_less_than_zero(startT) && step < 0)
2415 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002416 step = -step;
2417 SkDEBUGCODE(firstLoop = false;)
2418 }
2419 do {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002420 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002421 nextEnd = nextStart;
2422 do {
2423 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002424 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002425 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002426 #if 01
caryclark@google.com24bec792012-08-20 12:43:57 +00002427 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2428 break;
2429 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002430 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002431 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002432 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002433 SkDEBUGCODE(firstLoop = false;)
2434 step = -step;
2435 } while (true);
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002436 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002437 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2438 return other;
2439 }
2440 SkTDArray<Angle> angles;
2441 SkASSERT(startIndex - endIndex != 0);
2442 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2443 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002444 buildAngles(end, angles, false);
caryclark@google.com24bec792012-08-20 12:43:57 +00002445 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002446 bool sortable = SortAngles(angles, sorted);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002447 if (!sortable) {
2448 unsortable = true;
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002449 #if DEBUG_SORT
2450 debugShowSort(__FUNCTION__, sorted, findStartingEdge(sorted, startIndex, end), 0, 0);
2451 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002452 return NULL;
2453 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002454 int angleCount = angles.count();
2455 int firstIndex = findStartingEdge(sorted, startIndex, end);
2456 SkASSERT(firstIndex >= 0);
2457 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002458 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002459 #endif
2460 SkASSERT(sorted[firstIndex]->segment() == this);
2461 int nextIndex = firstIndex + 1;
2462 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002463 const Angle* foundAngle = NULL;
2464 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002465 Segment* nextSegment;
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002466 int activeCount = 0;
caryclark@google.com24bec792012-08-20 12:43:57 +00002467 do {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002468 SkASSERT(nextIndex != firstIndex);
caryclark@google.com24bec792012-08-20 12:43:57 +00002469 if (nextIndex == angleCount) {
2470 nextIndex = 0;
2471 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002472 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.com24bec792012-08-20 12:43:57 +00002473 nextSegment = nextAngle->segment();
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002474 ++activeCount;
2475 if (!foundAngle || (foundDone && activeCount & 1)) {
2476 if (nextSegment->tiny(nextAngle)) {
2477 unsortable = true;
2478 return NULL;
2479 }
2480 foundAngle = nextAngle;
2481 foundDone = nextSegment->done(nextAngle);
2482 }
2483 if (nextSegment->done()) {
2484 continue;
caryclark@google.com24bec792012-08-20 12:43:57 +00002485 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002486 } while (++nextIndex != lastIndex);
2487 markDone(SkMin32(startIndex, endIndex), 1);
2488 if (!foundAngle) {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002489 return NULL;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002490 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002491 nextStart = foundAngle->start();
2492 nextEnd = foundAngle->end();
2493 nextSegment = foundAngle->segment();
2494 #if DEBUG_WINDING
2495 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2496 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
2497 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002498 return nextSegment;
2499 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002500
2501 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2502 int angleCount = sorted.count();
2503 int firstIndex = -1;
2504 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2505 const Angle* angle = sorted[angleIndex];
2506 if (angle->segment() == this && angle->start() == end &&
2507 angle->end() == start) {
2508 firstIndex = angleIndex;
2509 break;
2510 }
2511 }
2512 return firstIndex;
2513 }
2514
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002515 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002516 // note that fOtherIndex isn't computed yet, so it can't be used here
caryclark@google.com4eeda372012-12-06 21:47:48 +00002517 void findTooCloseToCall() {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002518 int count = fTs.count();
2519 if (count < 3) { // require t=0, x, 1 at minimum
2520 return;
2521 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002522 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002523 int moCount;
2524 Span* match;
2525 Segment* mOther;
2526 do {
2527 match = &fTs[matchIndex];
2528 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002529 // FIXME: allow quads, cubics to be near coincident?
2530 if (mOther->fVerb == SkPath::kLine_Verb) {
2531 moCount = mOther->fTs.count();
2532 if (moCount >= 3) {
2533 break;
2534 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002535 }
2536 if (++matchIndex >= count) {
2537 return;
2538 }
2539 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002540 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002541 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002542 // look for a pair of nearby T values that map to the same (x,y) value
2543 // if found, see if the pair of other segments share a common point. If
2544 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002545 for (int index = matchIndex + 1; index < count; ++index) {
2546 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002547 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002548 continue;
2549 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002550 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002551 if (tOther->fVerb != SkPath::kLine_Verb) {
2552 continue; // FIXME: allow quads, cubics to be near coincident?
2553 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002554 int toCount = tOther->fTs.count();
2555 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002556 continue;
2557 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002558 const SkPoint* testPt = &xyAtT(test);
2559 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002560 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002561 moCount = toCount;
2562 match = test;
2563 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002564 matchPt = testPt;
2565 continue;
2566 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002567 int moStart = -1;
2568 int moEnd = -1;
2569 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002570 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002571 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002572 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002573 continue;
2574 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002575 if (moSpan.fOther == this) {
2576 if (moSpan.fOtherT == match->fT) {
2577 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002578 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002579 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002580 continue;
2581 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002582 if (moSpan.fOther == tOther) {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002583 if (tOther->windValueAt(moSpan.fOtherT) == 0) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002584 moStart = -1;
2585 break;
2586 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002587 SkASSERT(moEnd == -1);
2588 moEnd = moIndex;
2589 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002590 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002591 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002592 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002593 continue;
2594 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002595 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002596 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002597 continue;
2598 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002599 int toStart = -1;
2600 int toEnd = -1;
2601 double toStartT, toEndT;
2602 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2603 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002604 if (toSpan.fDone) {
2605 continue;
2606 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002607 if (toSpan.fOther == this) {
2608 if (toSpan.fOtherT == test->fT) {
2609 toStart = toIndex;
2610 toStartT = toSpan.fT;
2611 }
2612 continue;
2613 }
2614 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002615 if (mOther->windValueAt(toSpan.fOtherT) == 0) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002616 moStart = -1;
2617 break;
2618 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002619 SkASSERT(toEnd == -1);
2620 toEnd = toIndex;
2621 toEndT = toSpan.fT;
2622 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002623 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002624 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2625 if (toStart <= 0 || toEnd <= 0) {
2626 continue;
2627 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002628 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002629 continue;
2630 }
2631 // test to see if the segment between there and here is linear
2632 if (!mOther->isLinear(moStart, moEnd)
2633 || !tOther->isLinear(toStart, toEnd)) {
2634 continue;
2635 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002636 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002637 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002638 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002639 } else {
caryclark@google.com4eeda372012-12-06 21:47:48 +00002640 mOther->addTCoincident(moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002641 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002642 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002643 }
2644
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002645 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002646 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002647 // a) mark spans with either end unsortable as done, or
2648 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2649 // when encountering an unsortable span
2650
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002651 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2652 // and use more concise logic like the old edge walker code?
2653 // FIXME: this needs to deal with coincident edges
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002654 Segment* findTop(int& tIndex, int& endIndex, bool& unsortable, bool onlySortable) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002655 // iterate through T intersections and return topmost
2656 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002657 SkASSERT(!done());
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002658 int firstT = -1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002659 SkPoint topPt;
2660 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002661 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002662 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002663 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002664 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002665 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002666 const Span& span = fTs[index];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002667 if (onlySortable && (span.fUnsortableStart || lastUnsortable)) {
caryclark@google.comf839c032012-10-26 21:03:50 +00002668 goto next;
2669 }
2670 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002671 const SkPoint& intercept = xyAtT(&span);
2672 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2673 && topPt.fX > intercept.fX)) {
2674 topPt = intercept;
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002675 firstT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002676 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002677 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002678 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002679 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002680 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002681 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002682 SkASSERT(firstT >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002683 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002684 int step = 1;
2685 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002686 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002687 step = -1;
2688 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002689 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002690 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002691 // if the topmost T is not on end, or is three-way or more, find left
2692 // look for left-ness from tLeft to firstT (matching y of other)
2693 SkTDArray<Angle> angles;
2694 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002695 addTwoAngles(end, firstT, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002696 buildAngles(firstT, angles, true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002697 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002698 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002699 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002700 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002701 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002702 if (onlySortable && !sortable) {
2703 unsortable = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002704 return NULL;
2705 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002706 // skip edges that have already been processed
2707 firstT = -1;
2708 Segment* leftSegment;
2709 do {
2710 const Angle* angle = sorted[++firstT];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002711 SkASSERT(!onlySortable || !angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002712 leftSegment = angle->segment();
2713 tIndex = angle->end();
2714 endIndex = angle->start();
2715 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002716 SkASSERT(!leftSegment->fTs[SkMin32(tIndex, endIndex)].fTiny);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002717 return leftSegment;
2718 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002719
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002720 // FIXME: not crazy about this
2721 // when the intersections are performed, the other index is into an
2722 // incomplete array. as the array grows, the indices become incorrect
2723 // while the following fixes the indices up again, it isn't smart about
2724 // skipping segments whose indices are already correct
2725 // assuming we leave the code that wrote the index in the first place
2726 void fixOtherTIndex() {
2727 int iCount = fTs.count();
2728 for (int i = 0; i < iCount; ++i) {
2729 Span& iSpan = fTs[i];
2730 double oT = iSpan.fOtherT;
2731 Segment* other = iSpan.fOther;
2732 int oCount = other->fTs.count();
2733 for (int o = 0; o < oCount; ++o) {
2734 Span& oSpan = other->fTs[o];
2735 if (oT == oSpan.fT && this == oSpan.fOther) {
2736 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002737 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002738 }
2739 }
2740 }
2741 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002742
caryclark@google.com4eeda372012-12-06 21:47:48 +00002743 void init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002744 fDoneSpans = 0;
2745 fOperand = operand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00002746 fXor = evenOdd;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002747 fPts = pts;
2748 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002749 }
skia.committer@gmail.com8d83d0d2012-12-28 02:01:18 +00002750
caryclark@google.com3586ece2012-12-27 18:46:58 +00002751 void initWinding(int start, int end) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002752 int local = spanSign(start, end);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002753 int oppLocal = oppSign(start, end);
caryclark@google.com3586ece2012-12-27 18:46:58 +00002754 (void) markAndChaseWinding(start, end, local, oppLocal);
caryclark@google.com73ca6242013-01-17 21:02:47 +00002755 // OPTIMIZATION: the reverse mark and chase could skip the first marking
2756 (void) markAndChaseWinding(end, start, local, oppLocal);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002757 }
2758
caryclark@google.com3586ece2012-12-27 18:46:58 +00002759 void initWinding(int start, int end, int winding, int oppWinding) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002760 int local = spanSign(start, end);
caryclark@google.com3586ece2012-12-27 18:46:58 +00002761 if (local * winding >= 0) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002762 winding += local;
2763 }
2764 int oppLocal = oppSign(start, end);
2765 if (oppLocal * oppWinding >= 0) {
2766 oppWinding += oppLocal;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002767 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002768 (void) markAndChaseWinding(start, end, winding, oppWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002769 }
2770
caryclark@google.com3586ece2012-12-27 18:46:58 +00002771/*
2772when we start with a vertical intersect, we try to use the dx to determine if the edge is to
2773the left or the right of vertical. This determines if we need to add the span's
skia.committer@gmail.com8d83d0d2012-12-28 02:01:18 +00002774sign or not. However, this isn't enough.
2775If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
caryclark@google.com3586ece2012-12-27 18:46:58 +00002776If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
2777from has the same x direction as this span, the winding should change. If the dx is opposite, then
skia.committer@gmail.com8d83d0d2012-12-28 02:01:18 +00002778the same winding is shared by both.
caryclark@google.com3586ece2012-12-27 18:46:58 +00002779*/
2780 void initWinding(int start, int end, double tHit, int winding, SkScalar hitDx, int oppWind,
2781 SkScalar hitOppDx) {
2782 SkASSERT(hitDx || !winding);
2783 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, tHit);
2784 SkASSERT(dx);
2785 int windVal = windValue(SkMin32(start, end));
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002786 #if DEBUG_WINDING_AT_T
2787 SkDebugf("%s oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, winding,
2788 hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
2789 #endif
caryclark@google.com3586ece2012-12-27 18:46:58 +00002790 if (!winding) {
2791 winding = dx < 0 ? windVal : -windVal;
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002792 } else if (winding * dx < 0) {
caryclark@google.com3586ece2012-12-27 18:46:58 +00002793 int sideWind = winding + (dx < 0 ? windVal : -windVal);
2794 if (abs(winding) < abs(sideWind)) {
2795 winding = sideWind;
2796 }
2797 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +00002798 #if DEBUG_WINDING_AT_T
2799 SkDebugf(" winding=%d\n", winding);
2800 #endif
caryclark@google.com3586ece2012-12-27 18:46:58 +00002801 int oppLocal = oppSign(start, end);
2802 SkASSERT(hitOppDx || !oppWind || !oppLocal);
2803 int oppWindVal = oppValue(SkMin32(start, end));
2804 if (!oppWind) {
2805 oppWind = dx < 0 ? oppWindVal : -oppWindVal;
2806 } else if (hitOppDx * dx >= 0) {
2807 int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
2808 if (abs(oppWind) < abs(oppSideWind)) {
2809 oppWind = oppSideWind;
2810 }
2811 }
2812 (void) markAndChaseWinding(start, end, winding, oppWind);
2813 }
2814
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002815 bool intersected() const {
2816 return fTs.count() > 0;
2817 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002818
caryclark@google.com10227bf2012-12-28 22:10:41 +00002819 bool isCanceled(int tIndex) const {
2820 return fTs[tIndex].fWindValue == 0 && fTs[tIndex].fOppValue == 0;
2821 }
2822
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002823 bool isConnected(int startIndex, int endIndex) const {
2824 return fTs[startIndex].fWindSum != SK_MinS32
2825 || fTs[endIndex].fWindSum != SK_MinS32;
2826 }
2827
caryclark@google.com235f56a2012-09-14 14:19:30 +00002828 bool isHorizontal() const {
2829 return fBounds.fTop == fBounds.fBottom;
2830 }
2831
caryclark@google.com15fa1382012-05-07 20:49:36 +00002832 bool isLinear(int start, int end) const {
2833 if (fVerb == SkPath::kLine_Verb) {
2834 return true;
2835 }
2836 if (fVerb == SkPath::kQuad_Verb) {
2837 SkPoint qPart[3];
2838 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2839 return QuadIsLinear(qPart);
2840 } else {
2841 SkASSERT(fVerb == SkPath::kCubic_Verb);
2842 SkPoint cPart[4];
2843 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2844 return CubicIsLinear(cPart);
2845 }
2846 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002847
2848 // OPTIMIZE: successive calls could start were the last leaves off
2849 // or calls could specialize to walk forwards or backwards
2850 bool isMissing(double startT) const {
2851 size_t tCount = fTs.count();
2852 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002853 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002854 return false;
2855 }
2856 }
2857 return true;
2858 }
2859
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002860 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002861 int count = fTs.count();
2862 if (count == 2) {
2863 return true;
2864 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002865 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002866 if (approximately_less_than_zero(t)) {
2867 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002868 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002869 if (approximately_greater_than_one(t)) {
2870 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002871 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002872 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002873 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002874
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002875 bool isVertical() const {
2876 return fBounds.fLeft == fBounds.fRight;
2877 }
skia.committer@gmail.comb89a03c2012-12-22 02:02:33 +00002878
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002879 bool isVertical(int start, int end) const {
2880 return (*SegmentVertical[fVerb])(fPts, start, end);
2881 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002882
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002883 SkScalar leftMost(int start, int end) const {
2884 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2885 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002886
caryclark@google.com495f8e42012-05-31 13:13:11 +00002887 // this span is excluded by the winding rule -- chase the ends
2888 // as long as they are unambiguous to mark connections as done
2889 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002890 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002891 int index = angle->start();
2892 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002893 return markAndChaseDone(index, endIndex, winding);
2894 }
2895
caryclark@google.com31143cf2012-11-09 22:14:19 +00002896 Span* markAndChaseDone(int index, int endIndex, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002897 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002898 int min = SkMin32(index, endIndex);
2899 markDone(min, winding);
2900 Span* last;
2901 Segment* other = this;
2902 while ((other = other->nextChase(index, step, min, last))) {
2903 other->markDone(min, winding);
2904 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002905 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002906 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002907
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002908 Span* markAndChaseDoneBinary(const Angle* angle, int winding, int oppWinding) {
2909 int index = angle->start();
2910 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002911 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002912 int min = SkMin32(index, endIndex);
2913 markDoneBinary(min, winding, oppWinding);
2914 Span* last;
2915 Segment* other = this;
2916 while ((other = other->nextChase(index, step, min, last))) {
2917 other->markDoneBinary(min, winding, oppWinding);
2918 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002919 return last;
2920 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002921
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002922 Span* markAndChaseDoneBinary(int index, int endIndex) {
2923 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002924 int min = SkMin32(index, endIndex);
2925 markDoneBinary(min);
2926 Span* last;
2927 Segment* other = this;
2928 while ((other = other->nextChase(index, step, min, last))) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002929 if (other->done()) {
2930 return NULL;
2931 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002932 other->markDoneBinary(min);
2933 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002934 return last;
2935 }
2936
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002937 Span* markAndChaseDoneUnary(int index, int endIndex) {
2938 int step = SkSign32(endIndex - index);
2939 int min = SkMin32(index, endIndex);
2940 markDoneUnary(min);
2941 Span* last;
2942 Segment* other = this;
2943 while ((other = other->nextChase(index, step, min, last))) {
2944 if (other->done()) {
2945 return NULL;
2946 }
2947 other->markDoneUnary(min);
2948 }
2949 return last;
2950 }
2951
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002952 Span* markAndChaseDoneUnary(const Angle* angle, int winding) {
2953 int index = angle->start();
2954 int endIndex = angle->end();
2955 return markAndChaseDone(index, endIndex, winding);
2956 }
2957
caryclark@google.com4eeda372012-12-06 21:47:48 +00002958 Span* markAndChaseWinding(const Angle* angle, const int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002959 int index = angle->start();
2960 int endIndex = angle->end();
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002961 int step = SkSign32(endIndex - index);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002962 int min = SkMin32(index, endIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002963 markWinding(min, winding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002964 Span* last;
2965 Segment* other = this;
2966 while ((other = other->nextChase(index, step, min, last))) {
2967 if (other->fTs[min].fWindSum != SK_MinS32) {
2968 SkASSERT(other->fTs[min].fWindSum == winding);
2969 return NULL;
2970 }
2971 other->markWinding(min, winding);
2972 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002973 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002974 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002975
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002976 Span* markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002977 int min = SkMin32(index, endIndex);
2978 int step = SkSign32(endIndex - index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002979 markWinding(min, winding, oppWinding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002980 Span* last;
2981 Segment* other = this;
2982 while ((other = other->nextChase(index, step, min, last))) {
2983 if (other->fTs[min].fWindSum != SK_MinS32) {
2984 SkASSERT(other->fTs[min].fWindSum == winding);
2985 return NULL;
2986 }
2987 other->markWinding(min, winding, oppWinding);
2988 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002989 return last;
2990 }
2991
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002992 Span* markAndChaseWinding(const Angle* angle, int winding, int oppWinding) {
2993 int start = angle->start();
2994 int end = angle->end();
2995 return markAndChaseWinding(start, end, winding, oppWinding);
2996 }
2997
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002998 Span* markAngle(int maxWinding, int sumWinding, bool activeAngle, const Angle* angle) {
2999 SkASSERT(angle->segment() == this);
3000 if (useInnerWinding(maxWinding, sumWinding)) {
3001 maxWinding = sumWinding;
3002 }
3003 Span* last;
3004 if (activeAngle) {
3005 last = markAndChaseWinding(angle, maxWinding);
3006 } else {
3007 last = markAndChaseDoneUnary(angle, maxWinding);
3008 }
3009 return last;
3010 }
3011
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003012 Span* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
3013 bool activeAngle, const Angle* angle) {
3014 SkASSERT(angle->segment() == this);
3015 if (useInnerWinding(maxWinding, sumWinding)) {
3016 maxWinding = sumWinding;
3017 }
3018 if (oppMaxWinding != oppSumWinding && useInnerWinding(oppMaxWinding, oppSumWinding)) {
3019 oppMaxWinding = oppSumWinding;
3020 }
3021 Span* last;
3022 if (activeAngle) {
3023 last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
3024 } else {
3025 last = markAndChaseDoneBinary(angle, maxWinding, oppMaxWinding);
3026 }
3027 return last;
3028 }
3029
caryclark@google.com495f8e42012-05-31 13:13:11 +00003030 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003031 // This may be called when the segment is already marked done. While this
3032 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00003033 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003034 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00003035 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003036 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003037 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003038 double referenceT = fTs[index].fT;
3039 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00003040 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3041 markOneDone(__FUNCTION__, lesser, winding);
3042 }
3043 do {
3044 markOneDone(__FUNCTION__, index, winding);
3045 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00003046 }
3047
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003048 void markDoneBinary(int index, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003049 // SkASSERT(!done());
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00003050 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003051 double referenceT = fTs[index].fT;
3052 int lesser = index;
3053 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003054 markOneDoneBinary(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003055 }
3056 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003057 markOneDoneBinary(__FUNCTION__, index, winding, oppWinding);
3058 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3059 }
3060
3061 void markDoneBinary(int index) {
3062 double referenceT = fTs[index].fT;
3063 int lesser = index;
3064 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3065 markOneDoneBinary(__FUNCTION__, lesser);
3066 }
3067 do {
3068 markOneDoneBinary(__FUNCTION__, index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003069 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003070 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003071
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003072 void markDoneUnary(int index, int winding) {
3073 // SkASSERT(!done());
3074 SkASSERT(winding);
3075 double referenceT = fTs[index].fT;
3076 int lesser = index;
3077 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3078 markOneDoneUnary(__FUNCTION__, lesser, winding);
3079 }
3080 do {
3081 markOneDoneUnary(__FUNCTION__, index, winding);
3082 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3083 }
3084
3085 void markDoneUnary(int index) {
3086 double referenceT = fTs[index].fT;
3087 int lesser = index;
3088 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3089 markOneDoneUnary(__FUNCTION__, lesser);
3090 }
3091 do {
3092 markOneDoneUnary(__FUNCTION__, index);
3093 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3094 }
3095
caryclark@google.com24bec792012-08-20 12:43:57 +00003096 void markOneDone(const char* funName, int tIndex, int winding) {
3097 Span* span = markOneWinding(funName, tIndex, winding);
3098 if (!span) {
3099 return;
3100 }
3101 span->fDone = true;
3102 fDoneSpans++;
3103 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003104
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003105 void markOneDoneBinary(const char* funName, int tIndex) {
3106 Span* span = verifyOneWinding(funName, tIndex);
3107 if (!span) {
3108 return;
3109 }
3110 span->fDone = true;
3111 fDoneSpans++;
3112 }
3113
3114 void markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003115 Span* span = markOneWinding(funName, tIndex, winding, oppWinding);
3116 if (!span) {
3117 return;
3118 }
3119 span->fDone = true;
3120 fDoneSpans++;
3121 }
3122
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003123 void markOneDoneUnary(const char* funName, int tIndex) {
3124 Span* span = verifyOneWindingU(funName, tIndex);
3125 if (!span) {
3126 return;
3127 }
3128 span->fDone = true;
3129 fDoneSpans++;
3130 }
3131
3132 void markOneDoneUnary(const char* funName, int tIndex, int winding) {
3133 Span* span = markOneWinding(funName, tIndex, winding);
3134 if (!span) {
3135 return;
3136 }
3137 span->fDone = true;
3138 fDoneSpans++;
3139 }
3140
caryclark@google.com24bec792012-08-20 12:43:57 +00003141 Span* markOneWinding(const char* funName, int tIndex, int winding) {
3142 Span& span = fTs[tIndex];
3143 if (span.fDone) {
3144 return NULL;
3145 }
3146 #if DEBUG_MARK_DONE
3147 debugShowNewWinding(funName, span, winding);
3148 #endif
3149 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00003150 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00003151 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00003152 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00003153 span.fWindSum = winding;
3154 return &span;
3155 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00003156
caryclark@google.com31143cf2012-11-09 22:14:19 +00003157 Span* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding) {
3158 Span& span = fTs[tIndex];
3159 if (span.fDone) {
3160 return NULL;
3161 }
3162 #if DEBUG_MARK_DONE
3163 debugShowNewWinding(funName, span, winding, oppWinding);
3164 #endif
3165 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
3166 #ifdef SK_DEBUG
3167 SkASSERT(abs(winding) <= gDebugMaxWindSum);
3168 #endif
3169 span.fWindSum = winding;
3170 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
3171 #ifdef SK_DEBUG
3172 SkASSERT(abs(oppWinding) <= gDebugMaxWindSum);
3173 #endif
3174 span.fOppSum = oppWinding;
3175 return &span;
3176 }
3177
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003178 Span* verifyOneWinding(const char* funName, int tIndex) {
3179 Span& span = fTs[tIndex];
3180 if (span.fDone) {
3181 return NULL;
3182 }
3183 #if DEBUG_MARK_DONE
3184 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
3185 #endif
3186 SkASSERT(span.fWindSum != SK_MinS32);
3187 SkASSERT(span.fOppSum != SK_MinS32);
3188 return &span;
3189 }
3190
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003191 Span* verifyOneWindingU(const char* funName, int tIndex) {
3192 Span& span = fTs[tIndex];
3193 if (span.fDone) {
3194 return NULL;
3195 }
3196 #if DEBUG_MARK_DONE
3197 debugShowNewWinding(funName, span, span.fWindSum);
3198 #endif
3199 SkASSERT(span.fWindSum != SK_MinS32);
3200 return &span;
3201 }
3202
caryclark@google.comf839c032012-10-26 21:03:50 +00003203 // note that just because a span has one end that is unsortable, that's
3204 // not enough to mark it done. The other end may be sortable, allowing the
3205 // span to be added.
caryclark@google.com8f9f4682013-01-03 21:18:16 +00003206 // FIXME: if abs(start - end) > 1, mark intermediates as unsortable on both ends
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003207 void markUnsortable(int start, int end) {
3208 Span* span = &fTs[start];
3209 if (start < end) {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00003210#if DEBUG_UNSORTABLE
3211 SkDebugf("%s start id=%d [%d] (%1.9g,%1.9g)\n", __FUNCTION__, fID, start,
3212 xAtT(start), yAtT(start));
skia.committer@gmail.comd9f65e32013-01-04 12:07:46 +00003213#endif
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003214 span->fUnsortableStart = true;
3215 } else {
3216 --span;
caryclark@google.com8f9f4682013-01-03 21:18:16 +00003217#if DEBUG_UNSORTABLE
3218 SkDebugf("%s end id=%d [%d] (%1.9g,%1.9g) next:(%1.9g,%1.9g)\n", __FUNCTION__, fID,
3219 start - 1, xAtT(start - 1), yAtT(start - 1), xAtT(start), yAtT(start));
skia.committer@gmail.comd9f65e32013-01-04 12:07:46 +00003220#endif
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003221 span->fUnsortableEnd = true;
3222 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003223 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003224 return;
3225 }
3226 span->fDone = true;
3227 fDoneSpans++;
3228 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003229
caryclark@google.com59823f72012-08-09 18:17:47 +00003230 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003231 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003232 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003233 double referenceT = fTs[index].fT;
3234 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00003235 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3236 markOneWinding(__FUNCTION__, lesser, winding);
3237 }
3238 do {
3239 markOneWinding(__FUNCTION__, index, winding);
3240 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00003241 }
3242
3243 void markWinding(int index, int winding, int oppWinding) {
3244 // SkASSERT(!done());
caryclark@google.com4eeda372012-12-06 21:47:48 +00003245 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003246 double referenceT = fTs[index].fT;
3247 int lesser = index;
3248 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3249 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003250 }
3251 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003252 markOneWinding(__FUNCTION__, index, winding, oppWinding);
3253 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003254 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003255
caryclark@google.com2ddff932012-08-07 21:25:27 +00003256 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003257 int nextDoorWind = SK_MaxS32;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003258 int nextOppWind = SK_MaxS32;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003259 if (tIndex > 0) {
3260 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003261 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003262 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003263 nextOppWind = below.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003264 }
3265 }
3266 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3267 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003268 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003269 nextDoorWind = above.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003270 nextOppWind = above.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003271 }
3272 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003273 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
3274 const Span& below = fTs[tIndex - 1];
3275 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003276 nextOppWind = below.fOppValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003277 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003278 if (nextDoorWind != SK_MaxS32) {
3279 Span& newSpan = fTs[tIndex];
3280 newSpan.fWindValue = nextDoorWind;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003281 newSpan.fOppValue = nextOppWind;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003282 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003283 newSpan.fDone = true;
3284 ++fDoneSpans;
3285 }
3286 }
3287 }
3288
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003289 bool moreHorizontal(int index, int endIndex, bool& unsortable) const {
3290 // find bounds
3291 Bounds bounds;
3292 bounds.setPoint(xyAtT(index));
3293 bounds.add(xyAtT(endIndex));
3294 SkScalar width = bounds.width();
3295 SkScalar height = bounds.height();
3296 if (width > height) {
3297 if (approximately_negative(width)) {
3298 unsortable = true; // edge is too small to resolve meaningfully
3299 }
3300 return false;
3301 } else {
3302 if (approximately_negative(height)) {
3303 unsortable = true; // edge is too small to resolve meaningfully
3304 }
3305 return true;
3306 }
3307 }
3308
caryclark@google.com9764cc62012-07-12 19:29:45 +00003309 // return span if when chasing, two or more radiating spans are not done
3310 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
3311 // candidate and the remaining spans have windValue == 0 (canceled by
3312 // coincidence). The coincident edges could either be removed altogether,
3313 // or this code could be more complicated in detecting this case. Worth it?
3314 bool multipleSpans(int end) const {
3315 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003316 }
3317
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003318 bool nextCandidate(int& start, int& end) const {
caryclark@google.com10227bf2012-12-28 22:10:41 +00003319 while (fTs[end].fDone) {
3320 if (fTs[end].fT == 1) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003321 return false;
3322 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00003323 ++end;
3324 }
3325 start = end;
3326 end = nextExactSpan(start, 1);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003327 return true;
3328 }
3329
caryclark@google.com4eeda372012-12-06 21:47:48 +00003330 Segment* nextChase(int& index, const int step, int& min, Span*& last) const {
3331 int end = nextExactSpan(index, step);
3332 SkASSERT(end >= 0);
3333 if (multipleSpans(end)) {
3334 last = &fTs[end];
3335 return NULL;
3336 }
3337 const Span& endSpan = fTs[end];
3338 Segment* other = endSpan.fOther;
3339 index = endSpan.fOtherIndex;
3340 int otherEnd = other->nextExactSpan(index, step);
3341 min = SkMin32(index, otherEnd);
3342 return other;
3343 }
3344
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003345 // This has callers for two different situations: one establishes the end
3346 // of the current span, and one establishes the beginning of the next span
3347 // (thus the name). When this is looking for the end of the current span,
3348 // coincidence is found when the beginning Ts contain -step and the end
3349 // contains step. When it is looking for the beginning of the next, the
3350 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003351 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003352 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003353 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00003354 int count = fTs.count();
3355 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00003356 while (step > 0 ? ++to < count : --to >= 0) {
3357 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003358 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003359 continue;
3360 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003361 return to;
3362 }
3363 return -1;
3364 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00003365
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003366 // FIXME
3367 // this returns at any difference in T, vs. a preset minimum. It may be
3368 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003369 // OPTIMIZATION splitting this into separate loops for up/down steps
3370 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003371 int nextExactSpan(int from, int step) const {
3372 const Span& fromSpan = fTs[from];
3373 int count = fTs.count();
3374 int to = from;
3375 while (step > 0 ? ++to < count : --to >= 0) {
3376 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00003377 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003378 continue;
3379 }
3380 return to;
3381 }
3382 return -1;
3383 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003384
caryclark@google.com235f56a2012-09-14 14:19:30 +00003385 bool operand() const {
3386 return fOperand;
3387 }
3388
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003389 int oppSign(const Angle* angle) const {
3390 SkASSERT(angle->segment() == this);
3391 return oppSign(angle->start(), angle->end());
3392 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003393
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003394 int oppSign(int startIndex, int endIndex) const {
3395 int result = startIndex < endIndex ? -fTs[startIndex].fOppValue
3396 : fTs[endIndex].fOppValue;
3397#if DEBUG_WIND_BUMP
3398 SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
3399#endif
3400 return result;
3401 }
3402
caryclark@google.com31143cf2012-11-09 22:14:19 +00003403 int oppSum(int tIndex) const {
3404 return fTs[tIndex].fOppSum;
3405 }
3406
3407 int oppSum(const Angle* angle) const {
3408 int lesser = SkMin32(angle->start(), angle->end());
3409 return fTs[lesser].fOppSum;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003410 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003411
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003412 int oppValue(int tIndex) const {
3413 return fTs[tIndex].fOppValue;
3414 }
3415
caryclark@google.come7bd5f42012-12-13 19:47:53 +00003416 int oppValue(const Angle* angle) const {
3417 int lesser = SkMin32(angle->start(), angle->end());
3418 return fTs[lesser].fOppValue;
3419 }
3420
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003421 const SkPoint* pts() const {
3422 return fPts;
3423 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003424
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003425 void reset() {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003426 init(NULL, (SkPath::Verb) -1, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003427 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
3428 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003429 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00003430
caryclark@google.com4eeda372012-12-06 21:47:48 +00003431 void setOppXor(bool isOppXor) {
3432 fOppXor = isOppXor;
3433 }
3434
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003435 void setUpWinding(int index, int endIndex, int& maxWinding, int& sumWinding) {
3436 int deltaSum = spanSign(index, endIndex);
3437 maxWinding = sumWinding;
3438 sumWinding = sumWinding -= deltaSum;
3439 }
3440
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003441 void setUpWindings(int index, int endIndex, int& sumMiWinding, int& sumSuWinding,
3442 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
3443 int deltaSum = spanSign(index, endIndex);
3444 int oppDeltaSum = oppSign(index, endIndex);
3445 if (operand()) {
3446 maxWinding = sumSuWinding;
3447 sumWinding = sumSuWinding -= deltaSum;
3448 oppMaxWinding = sumMiWinding;
3449 oppSumWinding = sumMiWinding -= oppDeltaSum;
3450 } else {
3451 maxWinding = sumMiWinding;
3452 sumWinding = sumMiWinding -= deltaSum;
3453 oppMaxWinding = sumSuWinding;
3454 oppSumWinding = sumSuWinding -= oppDeltaSum;
3455 }
3456 }
3457
caryclark@google.comf839c032012-10-26 21:03:50 +00003458 // This marks all spans unsortable so that this info is available for early
3459 // exclusion in find top and others. This could be optimized to only mark
3460 // adjacent spans that unsortable. However, this makes it difficult to later
3461 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003462 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003463 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003464 int angleCount = angles.count();
3465 int angleIndex;
3466 angleList.setReserve(angleCount);
3467 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003468 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00003469 *angleList.append() = &angle;
3470 sortable &= !angle.unsortable();
3471 }
3472 if (sortable) {
3473 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3474 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3475 if (angles[angleIndex].unsortable()) {
3476 sortable = false;
3477 break;
3478 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003479 }
3480 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003481 if (!sortable) {
3482 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3483 Angle& angle = angles[angleIndex];
3484 angle.segment()->markUnsortable(angle.start(), angle.end());
3485 }
3486 }
3487 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003488 }
3489
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003490 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003491 const Span& span(int tIndex) const {
3492 return fTs[tIndex];
3493 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003494
caryclark@google.com235f56a2012-09-14 14:19:30 +00003495 int spanSign(const Angle* angle) const {
3496 SkASSERT(angle->segment() == this);
3497 return spanSign(angle->start(), angle->end());
3498 }
3499
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003500 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003501 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue
3502 : fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003503#if DEBUG_WIND_BUMP
3504 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3505#endif
3506 return result;
3507 }
3508
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003509 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003510 double t(int tIndex) const {
3511 return fTs[tIndex].fT;
3512 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003513
caryclark@google.com10227bf2012-12-28 22:10:41 +00003514 double tAtMid(int start, int end, double mid) const {
3515 return fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
3516 }
3517
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003518 bool tiny(const Angle* angle) const {
3519 int start = angle->start();
3520 int end = angle->end();
caryclark@google.comf839c032012-10-26 21:03:50 +00003521 const Span& mSpan = fTs[SkMin32(start, end)];
3522 return mSpan.fTiny;
3523 }
3524
caryclark@google.com18063442012-07-25 12:05:18 +00003525 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3526 double start) {
3527 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003528 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003529 *outsideTs.append() = end;
3530 *outsideTs.append() = start;
3531 }
3532 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00003533
caryclark@google.com24bec792012-08-20 12:43:57 +00003534 void undoneSpan(int& start, int& end) {
3535 size_t tCount = fTs.count();
3536 size_t index;
3537 for (index = 0; index < tCount; ++index) {
3538 if (!fTs[index].fDone) {
3539 break;
3540 }
3541 }
3542 SkASSERT(index < tCount - 1);
3543 start = index;
3544 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003545 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003546 SkASSERT(index < tCount);
3547 SkASSERT(index < tCount);
3548 end = index;
3549 }
caryclark@google.com18063442012-07-25 12:05:18 +00003550
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003551 bool unsortable(int index) const {
3552 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3553 }
3554
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003555 void updatePts(const SkPoint pts[]) {
3556 fPts = pts;
3557 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003558
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003559 int updateOppWinding(int index, int endIndex) const {
3560 int lesser = SkMin32(index, endIndex);
3561 int oppWinding = oppSum(lesser);
3562 int oppSpanWinding = oppSign(index, endIndex);
3563 if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)) {
3564 oppWinding -= oppSpanWinding;
3565 }
3566 return oppWinding;
3567 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00003568
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003569 int updateOppWinding(const Angle* angle) const {
3570 int startIndex = angle->start();
3571 int endIndex = angle->end();
3572 return updateOppWinding(endIndex, startIndex);
3573 }
3574
3575 int updateOppWindingReverse(const Angle* angle) const {
3576 int startIndex = angle->start();
3577 int endIndex = angle->end();
3578 return updateOppWinding(startIndex, endIndex);
3579 }
3580
3581 int updateWinding(int index, int endIndex) const {
3582 int lesser = SkMin32(index, endIndex);
3583 int winding = windSum(lesser);
3584 int spanWinding = spanSign(index, endIndex);
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00003585 if (winding && useInnerWinding(winding - spanWinding, winding)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003586 winding -= spanWinding;
3587 }
3588 return winding;
3589 }
3590
3591 int updateWinding(const Angle* angle) const {
3592 int startIndex = angle->start();
3593 int endIndex = angle->end();
3594 return updateWinding(endIndex, startIndex);
3595 }
3596
3597 int updateWindingReverse(const Angle* angle) const {
3598 int startIndex = angle->start();
3599 int endIndex = angle->end();
3600 return updateWinding(startIndex, endIndex);
3601 }
3602
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003603 SkPath::Verb verb() const {
3604 return fVerb;
3605 }
skia.committer@gmail.comb89a03c2012-12-22 02:02:33 +00003606
skia.committer@gmail.com8d83d0d2012-12-28 02:01:18 +00003607 int windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar& dx) const {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003608 if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
3609 return SK_MinS32;
3610 }
3611 int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
3612 SkASSERT(winding != SK_MinS32);
3613 int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
caryclark@google.com8f9f4682013-01-03 21:18:16 +00003614 #if DEBUG_WINDING_AT_T
3615 SkDebugf("%s oldWinding=%d windValue=%d", __FUNCTION__, winding, windVal);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003616 #endif
3617 // see if a + change in T results in a +/- change in X (compute x'(T))
caryclark@google.com3586ece2012-12-27 18:46:58 +00003618 dx = (*SegmentDXAtT[fVerb])(fPts, tHit);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003619 if (fVerb > SkPath::kLine_Verb && approximately_zero(dx)) {
3620 dx = fPts[2].fX - fPts[1].fX - dx;
3621 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003622 if (dx == 0) {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00003623 #if DEBUG_WINDING_AT_T
3624 SkDebugf(" dx=0 winding=SK_MinS32\n");
3625 #endif
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003626 return SK_MinS32;
3627 }
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003628 if (winding * dx > 0) { // if same signs, result is negative
3629 winding += dx > 0 ? -windVal : windVal;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003630 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +00003631 #if DEBUG_WINDING_AT_T
3632 SkDebugf(" dx=%c winding=%d\n", dx > 0 ? '+' : '-', winding);
3633 #endif
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003634 return winding;
3635 }
3636
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003637 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003638 return fTs[tIndex].fWindSum;
3639 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003640
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003641 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003642 int start = angle->start();
3643 int end = angle->end();
3644 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003645 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003646 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003647
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003648 int windValue(int tIndex) const {
3649 return fTs[tIndex].fWindValue;
3650 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003651
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003652 int windValue(const Angle* angle) const {
3653 int start = angle->start();
3654 int end = angle->end();
3655 int index = SkMin32(start, end);
3656 return windValue(index);
3657 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003658
caryclark@google.com8f9f4682013-01-03 21:18:16 +00003659 int windValueAt(double t) const {
3660 int count = fTs.count();
3661 for (int index = 0; index < count; ++index) {
3662 if (fTs[index].fT == t) {
3663 return fTs[index].fWindValue;
3664 }
3665 }
3666 SkASSERT(0);
3667 return 0;
3668 }
3669
caryclark@google.com3586ece2012-12-27 18:46:58 +00003670 SkScalar xAtT(int index) const {
3671 return xAtT(&fTs[index]);
3672 }
3673
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003674 SkScalar xAtT(const Span* span) const {
3675 return xyAtT(span).fX;
3676 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003677
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003678 const SkPoint& xyAtT(int index) const {
3679 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003680 }
3681
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003682 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003683 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003684 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003685 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003686 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003687 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003688 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003689 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003690 }
3691 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003692 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003693 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003694
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003695 // used only by right angle winding finding
caryclark@google.com10227bf2012-12-28 22:10:41 +00003696 void xyAtT(double mid, SkPoint& pt) const {
3697 (*SegmentXYAtT[fVerb])(fPts, mid, &pt);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003698 }
3699
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003700 SkScalar yAtT(int index) const {
3701 return yAtT(&fTs[index]);
3702 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003703
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003704 SkScalar yAtT(const Span* span) const {
3705 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003706 }
3707
caryclark@google.com4eeda372012-12-06 21:47:48 +00003708 void zeroCoincidentOpp(Span* oTest, int index) {
3709 Span* const test = &fTs[index];
3710 Span* end = test;
3711 do {
3712 end->fOppValue = 0;
3713 end = &fTs[++index];
3714 } while (approximately_negative(end->fT - test->fT));
3715 }
3716
3717 void zeroCoincidentOther(Span* test, const double tRatio, const double oEndT, int oIndex) {
3718 Span* const oTest = &fTs[oIndex];
3719 Span* oEnd = oTest;
3720 const double startT = test->fT;
3721 const double oStartT = oTest->fT;
3722 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
3723 while (!approximately_negative(oEndT - oEnd->fT)
3724 && approximately_negative(oEnd->fT - otherTMatch)) {
3725 oEnd->fOppValue = 0;
3726 oEnd = &fTs[++oIndex];
3727 }
3728 }
3729
3730 void zeroSpan(Span* span) {
3731 SkASSERT(span->fWindValue > 0 || span->fOppValue > 0);
caryclark@google.com6ec15262012-11-16 20:16:50 +00003732 span->fWindValue = 0;
caryclark@google.com729e1c42012-11-21 21:36:34 +00003733 span->fOppValue = 0;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003734 SkASSERT(!span->fDone);
3735 span->fDone = true;
3736 ++fDoneSpans;
caryclark@google.com6ec15262012-11-16 20:16:50 +00003737 }
3738
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003739#if DEBUG_DUMP
3740 void dump() const {
3741 const char className[] = "Segment";
3742 const int tab = 4;
3743 for (int i = 0; i < fTs.count(); ++i) {
3744 SkPoint out;
3745 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3746 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003747 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003748 tab + sizeof(className), className, fID,
3749 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003750 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003751 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003752 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003753 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003754 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003755 }
3756#endif
3757
caryclark@google.com47580692012-07-23 12:14:49 +00003758#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003759 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003760 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003761 for (int i = 0; i < fTs.count(); ++i) {
3762 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3763 return;
3764 }
3765 }
3766 SkASSERT(0);
3767 }
3768#endif
3769
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003770#if DEBUG_DUMP
3771 int debugID() const {
3772 return fID;
3773 }
3774#endif
3775
caryclark@google.com24bec792012-08-20 12:43:57 +00003776#if DEBUG_WINDING
3777 void debugShowSums() const {
3778 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3779 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3780 for (int i = 0; i < fTs.count(); ++i) {
3781 const Span& span = fTs[i];
3782 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3783 if (span.fWindSum == SK_MinS32) {
3784 SkDebugf("?");
3785 } else {
3786 SkDebugf("%d", span.fWindSum);
3787 }
3788 SkDebugf("]");
3789 }
3790 SkDebugf("\n");
3791 }
3792#endif
3793
caryclark@google.comcc905052012-07-25 20:59:42 +00003794#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003795 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003796 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003797 int lastWind = -1;
3798 int lastOpp = -1;
3799 double lastT = -1;
3800 int i;
3801 for (i = 0; i < fTs.count(); ++i) {
3802 bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
3803 || lastOpp != fTs[i].fOppValue;
3804 if (change && lastWind >= 0) {
3805 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3806 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3807 }
3808 if (change) {
3809 SkDebugf(" [o=%d", fTs[i].fOther->fID);
3810 lastWind = fTs[i].fWindValue;
3811 lastOpp = fTs[i].fOppValue;
3812 lastT = fTs[i].fT;
3813 } else {
3814 SkDebugf(",%d", fTs[i].fOther->fID);
3815 }
3816 }
3817 if (i <= 0) {
3818 return;
3819 }
3820 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3821 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3822 if (fOperand) {
3823 SkDebugf(" operand");
3824 }
3825 if (done()) {
3826 SkDebugf(" done");
caryclark@google.com47580692012-07-23 12:14:49 +00003827 }
3828 SkDebugf("\n");
3829 }
3830#endif
3831
caryclark@google.com027de222012-07-12 12:52:50 +00003832#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003833 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003834 if (done()) {
3835 return;
3836 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003837#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3838 int lastId = -1;
3839 double lastT = -1;
3840#endif
caryclark@google.com027de222012-07-12 12:52:50 +00003841 for (int i = 0; i < fTs.count(); ++i) {
3842 if (fTs[i].fDone) {
3843 continue;
3844 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003845#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3846 if (lastId == fID && lastT == fTs[i].fT) {
3847 continue;
3848 }
3849 lastId = fID;
3850 lastT = fTs[i].fT;
3851#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003852 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003853 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3854 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3855 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3856 }
3857 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003858 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003859 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003860 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003861 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3862 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3863 if (fTs[i].fWindSum == SK_MinS32) {
3864 SkDebugf("?");
3865 } else {
3866 SkDebugf("%d", fTs[i].fWindSum);
3867 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003868 SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003869 }
3870 }
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +00003871
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003872 // This isn't useful yet -- but leaving it in for now in case i think of something
3873 // to use it for
3874 void validateActiveSpans() const {
3875 if (done()) {
3876 return;
3877 }
3878 int tCount = fTs.count();
3879 for (int index = 0; index < tCount; ++index) {
3880 if (fTs[index].fDone) {
3881 continue;
3882 }
3883 // count number of connections which are not done
3884 int first = index;
3885 double baseT = fTs[index].fT;
3886 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3887 --first;
3888 }
3889 int last = index;
3890 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3891 ++last;
3892 }
3893 int connections = 0;
3894 connections += first > 0 && !fTs[first - 1].fDone;
3895 for (int test = first; test <= last; ++test) {
3896 connections += !fTs[test].fDone;
3897 const Segment* other = fTs[test].fOther;
3898 int oIndex = fTs[test].fOtherIndex;
3899 connections += !other->fTs[oIndex].fDone;
3900 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3901 }
3902 // SkASSERT(!(connections & 1));
3903 }
3904 }
caryclark@google.com027de222012-07-12 12:52:50 +00003905#endif
3906
caryclark@google.com0c803d02012-08-06 11:15:47 +00003907#if DEBUG_MARK_DONE
3908 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3909 const SkPoint& pt = xyAtT(&span);
3910 SkDebugf("%s id=%d", fun, fID);
3911 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3912 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3913 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3914 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003915 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3916 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3917 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3918 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003919 if (span.fWindSum == SK_MinS32) {
3920 SkDebugf("?");
3921 } else {
3922 SkDebugf("%d", span.fWindSum);
3923 }
3924 SkDebugf(" windValue=%d\n", span.fWindValue);
3925 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003926
3927 void debugShowNewWinding(const char* fun, const Span& span, int winding, int oppWinding) {
3928 const SkPoint& pt = xyAtT(&span);
3929 SkDebugf("%s id=%d", fun, fID);
3930 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3931 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3932 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3933 }
3934 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3935 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3936 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d newOppSum=%d oppSum=",
3937 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
3938 winding, oppWinding);
3939 if (span.fOppSum == SK_MinS32) {
3940 SkDebugf("?");
3941 } else {
3942 SkDebugf("%d", span.fOppSum);
3943 }
3944 SkDebugf(" windSum=");
3945 if (span.fWindSum == SK_MinS32) {
3946 SkDebugf("?");
3947 } else {
3948 SkDebugf("%d", span.fWindSum);
3949 }
3950 SkDebugf(" windValue=%d\n", span.fWindValue);
3951 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003952#endif
3953
caryclark@google.com47580692012-07-23 12:14:49 +00003954#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003955 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com31143cf2012-11-09 22:14:19 +00003956 const int contourWinding, const int oppContourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003957 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003958 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003959 int lastSum = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003960 int oppLastSum = oppContourWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003961 const Angle* firstAngle = angles[first];
3962 int windSum = lastSum - spanSign(firstAngle);
3963 int oppoSign = oppSign(firstAngle);
3964 int oppWindSum = oppLastSum - oppoSign;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003965 SkDebugf("%s %s contourWinding=%d oppContourWinding=%d sign=%d\n", fun, __FUNCTION__,
3966 contourWinding, oppContourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003967 int index = first;
3968 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003969 do {
3970 const Angle& angle = *angles[index];
3971 const Segment& segment = *angle.segment();
3972 int start = angle.start();
3973 int end = angle.end();
3974 const Span& sSpan = segment.fTs[start];
3975 const Span& eSpan = segment.fTs[end];
3976 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com31143cf2012-11-09 22:14:19 +00003977 bool opp = segment.fOperand ^ fOperand;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003978 if (!firstTime) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003979 oppoSign = segment.oppSign(&angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003980 if (opp) {
3981 oppLastSum = oppWindSum;
3982 oppWindSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003983 if (oppoSign) {
3984 lastSum = windSum;
3985 windSum -= oppoSign;
3986 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003987 } else {
3988 lastSum = windSum;
3989 windSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003990 if (oppoSign) {
3991 oppLastSum = oppWindSum;
3992 oppWindSum -= oppoSign;
3993 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003994 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00003995 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003996 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 +00003997 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003998 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003999 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004000 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
4001 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
4002 mSpan.fWindValue);
4003 if (mSpan.fWindSum == SK_MinS32) {
4004 SkDebugf("?");
4005 } else {
4006 SkDebugf("%d", mSpan.fWindSum);
4007 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004008 int last, wind;
4009 if (opp) {
4010 last = oppLastSum;
4011 wind = oppWindSum;
4012 } else {
4013 last = lastSum;
4014 wind = windSum;
4015 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004016 if (!oppoSign) {
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00004017 SkDebugf(" %d->%d (max=%d)", last, wind,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004018 useInnerWinding(last, wind) ? wind : last);
4019 } else {
4020 SkDebugf(" %d->%d (%d->%d)", last, wind, opp ? lastSum : oppLastSum,
4021 opp ? windSum : oppWindSum);
4022 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004023 SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004024#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00004025 angle.debugShow(segment.xyAtT(&sSpan));
4026#endif
caryclark@google.com47580692012-07-23 12:14:49 +00004027 ++index;
4028 if (index == angles.count()) {
4029 index = 0;
4030 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004031 if (firstTime) {
4032 firstTime = false;
4033 }
caryclark@google.com47580692012-07-23 12:14:49 +00004034 } while (index != first);
4035 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00004036
4037 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first) {
4038 const Angle* firstAngle = angles[first];
4039 const Segment* segment = firstAngle->segment();
4040 int winding = segment->updateWinding(firstAngle);
4041 int oppWinding = segment->updateOppWinding(firstAngle);
4042 debugShowSort(fun, angles, first, winding, oppWinding);
4043 }
4044
caryclark@google.com47580692012-07-23 12:14:49 +00004045#endif
4046
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004047#if DEBUG_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004048 static char as_digit(int value) {
4049 return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
4050 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00004051#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004052
caryclark@google.com729e1c42012-11-21 21:36:34 +00004053#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004054 int debugShowWindingValues(int slotCount, int ofInterest) const {
4055 if (!(1 << fID & ofInterest)) {
4056 return 0;
4057 }
4058 int sum = 0;
4059 SkTDArray<char> slots;
4060 slots.setCount(slotCount * 2);
4061 memset(slots.begin(), ' ', slotCount * 2);
4062 for (int i = 0; i < fTs.count(); ++i) {
4063 // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
4064 // continue;
4065 // }
4066 sum += fTs[i].fWindValue;
4067 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
4068 sum += fTs[i].fOppValue;
4069 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
4070 }
4071 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
4072 slots.begin() + slotCount);
4073 return sum;
4074 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00004075#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004076
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004077private:
4078 const SkPoint* fPts;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004079 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004080 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com4eeda372012-12-06 21:47:48 +00004081 // OPTIMIZATION: could pack donespans, verb, operand, xor into 1 int-sized value
caryclark@google.com24bec792012-08-20 12:43:57 +00004082 int fDoneSpans; // quick check that segment is finished
caryclark@google.com4eeda372012-12-06 21:47:48 +00004083 // OPTIMIZATION: force the following to be byte-sized
4084 SkPath::Verb fVerb;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004085 bool fOperand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00004086 bool fXor; // set if original contour had even-odd fill
4087 bool fOppXor; // set if opposite operand had even-odd fill
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004088#if DEBUG_DUMP
4089 int fID;
4090#endif
4091};
4092
caryclark@google.comb9738012012-07-03 19:53:30 +00004093class Contour;
4094
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004095struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00004096 Contour* fContours[2];
4097 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004098 double fTs[2][2];
4099};
4100
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004101class Contour {
4102public:
4103 Contour() {
4104 reset();
4105#if DEBUG_DUMP
4106 fID = ++gContourID;
4107#endif
4108 }
4109
4110 bool operator<(const Contour& rh) const {
4111 return fBounds.fTop == rh.fBounds.fTop
4112 ? fBounds.fLeft < rh.fBounds.fLeft
4113 : fBounds.fTop < rh.fBounds.fTop;
4114 }
4115
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004116 void addCoincident(int index, Contour* other, int otherIndex,
4117 const Intersections& ts, bool swap) {
4118 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004119 coincidence.fContours[0] = this; // FIXME: no need to store
caryclark@google.comb9738012012-07-03 19:53:30 +00004120 coincidence.fContours[1] = other;
4121 coincidence.fSegments[0] = index;
4122 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00004123 if (fSegments[index].verb() == SkPath::kLine_Verb &&
4124 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
4125 // FIXME: coincident lines use legacy Ts instead of coincident Ts
4126 coincidence.fTs[swap][0] = ts.fT[0][0];
4127 coincidence.fTs[swap][1] = ts.fT[0][1];
4128 coincidence.fTs[!swap][0] = ts.fT[1][0];
4129 coincidence.fTs[!swap][1] = ts.fT[1][1];
4130 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
4131 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
4132 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
4133 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
4134 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
4135 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
4136 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004137 }
4138
4139 void addCross(const Contour* crosser) {
4140#ifdef DEBUG_CROSS
4141 for (int index = 0; index < fCrosses.count(); ++index) {
4142 SkASSERT(fCrosses[index] != crosser);
4143 }
4144#endif
4145 *fCrosses.append() = crosser;
4146 }
4147
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004148 void addCubic(const SkPoint pts[4]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004149 fSegments.push_back().addCubic(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004150 fContainsCurves = true;
4151 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004152
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004153 int addLine(const SkPoint pts[2]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004154 fSegments.push_back().addLine(pts, fOperand, fXor);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004155 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004156 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004157
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004158 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
4159 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
4160 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004161
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004162 int addQuad(const SkPoint pts[3]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004163 fSegments.push_back().addQuad(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004164 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004165 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004166 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004167
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004168 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
4169 containsIntercepts();
4170 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
4171 }
4172
caryclark@google.com73ca6242013-01-17 21:02:47 +00004173 int addUnsortableT(int segIndex, double newT, Contour* other, int otherIndex, bool start) {
4174 return fSegments[segIndex].addUnsortableT(newT, &other->fSegments[otherIndex], start);
4175 }
4176
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004177 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004178 return fBounds;
4179 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004180
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004181 void complete() {
4182 setBounds();
4183 fContainsIntercepts = false;
4184 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004185
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004186 void containsIntercepts() {
4187 fContainsIntercepts = true;
4188 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004189
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004190 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004191 for (int index = 0; index < fCrosses.count(); ++index) {
4192 if (fCrosses[index] == crosser) {
4193 return true;
4194 }
4195 }
4196 return false;
4197 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00004198
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004199 bool done() const {
4200 return fDone;
4201 }
4202
caryclark@google.comf839c032012-10-26 21:03:50 +00004203 const SkPoint& end() const {
4204 const Segment& segment = fSegments.back();
4205 return segment.pts()[segment.verb()];
4206 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004207
caryclark@google.com4eeda372012-12-06 21:47:48 +00004208 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004209 int segmentCount = fSegments.count();
4210 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004211 fSegments[sIndex].findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004212 }
4213 }
4214
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004215 void fixOtherTIndex() {
4216 int segmentCount = fSegments.count();
4217 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
4218 fSegments[sIndex].fixOtherTIndex();
4219 }
4220 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00004221
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004222 Segment* nonVerticalSegment(int& start, int& end) {
4223 int segmentCount = fSortedSegments.count();
4224 SkASSERT(segmentCount > 0);
4225 for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
4226 Segment* testSegment = fSortedSegments[sortedIndex];
4227 if (testSegment->done()) {
4228 continue;
4229 }
4230 start = end = 0;
4231 while (testSegment->nextCandidate(start, end)) {
4232 if (!testSegment->isVertical(start, end)) {
4233 return testSegment;
4234 }
4235 }
4236 }
4237 return NULL;
4238 }
4239
caryclark@google.com31143cf2012-11-09 22:14:19 +00004240 bool operand() const {
4241 return fOperand;
4242 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004243
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004244 void reset() {
4245 fSegments.reset();
4246 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004247 fContainsCurves = fContainsIntercepts = fDone = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004248 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004249
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004250 void resolveCoincidence(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004251 int count = fCoincidences.count();
4252 for (int index = 0; index < count; ++index) {
4253 Coincidence& coincidence = fCoincidences[index];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004254 SkASSERT(coincidence.fContours[0] == this);
caryclark@google.comb9738012012-07-03 19:53:30 +00004255 int thisIndex = coincidence.fSegments[0];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004256 Segment& thisOne = fSegments[thisIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004257 Contour* otherContour = coincidence.fContours[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00004258 int otherIndex = coincidence.fSegments[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00004259 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004260 if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004261 continue;
4262 }
caryclark@google.com47580692012-07-23 12:14:49 +00004263 #if DEBUG_CONCIDENT
4264 thisOne.debugShowTs();
4265 other.debugShowTs();
4266 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004267 double startT = coincidence.fTs[0][0];
4268 double endT = coincidence.fTs[0][1];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004269 bool cancelers = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004270 if (startT > endT) {
4271 SkTSwap<double>(startT, endT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004272 cancelers ^= true; // FIXME: just assign true
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004273 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004274 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004275 double oStartT = coincidence.fTs[1][0];
4276 double oEndT = coincidence.fTs[1][1];
4277 if (oStartT > oEndT) {
4278 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004279 cancelers ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004280 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004281 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00004282 bool opp = fOperand ^ otherContour->fOperand;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004283 if (cancelers && !opp) {
4284 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00004285 if (startT > 0 || oEndT < 1
4286 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
4287 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004288 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004289 if (oStartT > 0 || endT < 1
4290 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
4291 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004292 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004293 if (!thisOne.done() && !other.done()) {
4294 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
4295 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004296 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004297 if (startT > 0 || oStartT > 0
4298 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00004299 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004300 }
caryclark@google.com200c2112012-08-03 15:05:04 +00004301 if (endT < 1 || oEndT < 1
4302 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00004303 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004304 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004305 if (!thisOne.done() && !other.done()) {
4306 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
4307 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004308 }
caryclark@google.com47580692012-07-23 12:14:49 +00004309 #if DEBUG_CONCIDENT
4310 thisOne.debugShowTs();
4311 other.debugShowTs();
4312 #endif
caryclark@google.com729e1c42012-11-21 21:36:34 +00004313 #if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004314 debugShowWindingValues(contourList);
4315 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004316 }
4317 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004318
caryclark@google.com8f9f4682013-01-03 21:18:16 +00004319 // first pass, add missing T values
4320 // second pass, determine winding values of overlaps
4321 void addCoincidentPoints() {
4322 int count = fCoincidences.count();
4323 for (int index = 0; index < count; ++index) {
4324 Coincidence& coincidence = fCoincidences[index];
4325 SkASSERT(coincidence.fContours[0] == this);
4326 int thisIndex = coincidence.fSegments[0];
4327 Segment& thisOne = fSegments[thisIndex];
4328 Contour* otherContour = coincidence.fContours[1];
4329 int otherIndex = coincidence.fSegments[1];
4330 Segment& other = otherContour->fSegments[otherIndex];
4331 if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
4332 // OPTIMIZATION: remove from array
4333 continue;
4334 }
4335 #if DEBUG_CONCIDENT
4336 thisOne.debugShowTs();
4337 other.debugShowTs();
4338 #endif
4339 double startT = coincidence.fTs[0][0];
4340 double endT = coincidence.fTs[0][1];
4341 bool cancelers;
4342 if ((cancelers = startT > endT)) {
4343 SkTSwap<double>(startT, endT);
4344 }
4345 SkASSERT(!approximately_negative(endT - startT));
4346 double oStartT = coincidence.fTs[1][0];
4347 double oEndT = coincidence.fTs[1][1];
4348 if (oStartT > oEndT) {
4349 SkTSwap<double>(oStartT, oEndT);
4350 cancelers ^= true;
4351 }
4352 SkASSERT(!approximately_negative(oEndT - oStartT));
4353 bool opp = fOperand ^ otherContour->fOperand;
4354 if (cancelers && !opp) {
4355 // make sure startT and endT have t entries
4356 if (startT > 0 || oEndT < 1
4357 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
4358 thisOne.addTPair(startT, other, oEndT, true);
4359 }
4360 if (oStartT > 0 || endT < 1
4361 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
4362 other.addTPair(oStartT, thisOne, endT, true);
4363 }
4364 } else {
4365 if (startT > 0 || oStartT > 0
4366 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
4367 thisOne.addTPair(startT, other, oStartT, true);
4368 }
4369 if (endT < 1 || oEndT < 1
4370 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
4371 other.addTPair(oEndT, thisOne, endT, true);
4372 }
4373 }
4374 #if DEBUG_CONCIDENT
4375 thisOne.debugShowTs();
4376 other.debugShowTs();
4377 #endif
4378 }
4379 }
skia.committer@gmail.comd9f65e32013-01-04 12:07:46 +00004380
caryclark@google.com8f9f4682013-01-03 21:18:16 +00004381 void calcCoincidentWinding() {
4382 int count = fCoincidences.count();
4383 for (int index = 0; index < count; ++index) {
4384 Coincidence& coincidence = fCoincidences[index];
4385 SkASSERT(coincidence.fContours[0] == this);
4386 int thisIndex = coincidence.fSegments[0];
4387 Segment& thisOne = fSegments[thisIndex];
4388 if (thisOne.done()) {
4389 continue;
4390 }
4391 Contour* otherContour = coincidence.fContours[1];
4392 int otherIndex = coincidence.fSegments[1];
4393 Segment& other = otherContour->fSegments[otherIndex];
4394 if (other.done()) {
4395 continue;
4396 }
4397 double startT = coincidence.fTs[0][0];
4398 double endT = coincidence.fTs[0][1];
4399 bool cancelers;
4400 if ((cancelers = startT > endT)) {
4401 SkTSwap<double>(startT, endT);
4402 }
4403 SkASSERT(!approximately_negative(endT - startT));
4404 double oStartT = coincidence.fTs[1][0];
4405 double oEndT = coincidence.fTs[1][1];
4406 if (oStartT > oEndT) {
4407 SkTSwap<double>(oStartT, oEndT);
4408 cancelers ^= true;
4409 }
4410 SkASSERT(!approximately_negative(oEndT - oStartT));
4411 bool opp = fOperand ^ otherContour->fOperand;
4412 if (cancelers && !opp) {
4413 // make sure startT and endT have t entries
4414 if (!thisOne.done() && !other.done()) {
4415 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
4416 }
4417 } else {
4418 if (!thisOne.done() && !other.done()) {
4419 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
4420 }
4421 }
4422 #if DEBUG_CONCIDENT
4423 thisOne.debugShowTs();
4424 other.debugShowTs();
4425 #endif
4426 }
4427 }
4428
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004429 SkTArray<Segment>& segments() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004430 return fSegments;
4431 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004432
caryclark@google.com235f56a2012-09-14 14:19:30 +00004433 void setOperand(bool isOp) {
4434 fOperand = isOp;
4435 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00004436
caryclark@google.com4eeda372012-12-06 21:47:48 +00004437 void setOppXor(bool isOppXor) {
4438 fOppXor = isOppXor;
4439 int segmentCount = fSegments.count();
4440 for (int test = 0; test < segmentCount; ++test) {
4441 fSegments[test].setOppXor(isOppXor);
4442 }
4443 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004444
caryclark@google.com235f56a2012-09-14 14:19:30 +00004445 void setXor(bool isXor) {
4446 fXor = isXor;
4447 }
4448
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004449 void sortSegments() {
4450 int segmentCount = fSegments.count();
4451 fSortedSegments.setReserve(segmentCount);
4452 for (int test = 0; test < segmentCount; ++test) {
4453 *fSortedSegments.append() = &fSegments[test];
4454 }
4455 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
4456 fFirstSorted = 0;
4457 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004458
caryclark@google.comf839c032012-10-26 21:03:50 +00004459 const SkPoint& start() const {
4460 return fSegments.front().pts()[0];
4461 }
4462
4463 void toPath(PathWrapper& path) const {
4464 int segmentCount = fSegments.count();
4465 const SkPoint& pt = fSegments.front().pts()[0];
4466 path.deferredMove(pt);
4467 for (int test = 0; test < segmentCount; ++test) {
4468 fSegments[test].addCurveTo(0, 1, path, true);
4469 }
4470 path.close();
4471 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00004472
caryclark@google.comf839c032012-10-26 21:03:50 +00004473 void toPartialBackward(PathWrapper& path) const {
4474 int segmentCount = fSegments.count();
4475 for (int test = segmentCount - 1; test >= 0; --test) {
4476 fSegments[test].addCurveTo(1, 0, path, true);
4477 }
4478 }
4479
4480 void toPartialForward(PathWrapper& path) const {
4481 int segmentCount = fSegments.count();
4482 for (int test = 0; test < segmentCount; ++test) {
4483 fSegments[test].addCurveTo(0, 1, path, true);
4484 }
4485 }
4486
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004487 void topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY, Segment*& topStart) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004488 int segmentCount = fSortedSegments.count();
4489 SkASSERT(segmentCount > 0);
caryclark@google.comf839c032012-10-26 21:03:50 +00004490 int sortedIndex = fFirstSorted;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004491 fDone = true; // may be cleared below
caryclark@google.comf839c032012-10-26 21:03:50 +00004492 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
4493 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004494 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00004495 if (sortedIndex == fFirstSorted) {
4496 ++fFirstSorted;
4497 }
4498 continue;
4499 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004500 fDone = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00004501 SkPoint testXY;
4502 testSegment->activeLeftTop(testXY);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004503 if (topStart) {
4504 if (testXY.fY < topLeft.fY) {
4505 continue;
4506 }
4507 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
4508 continue;
4509 }
4510 if (bestXY.fY < testXY.fY) {
4511 continue;
4512 }
4513 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
4514 continue;
4515 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004516 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004517 topStart = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00004518 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004519 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004520 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004521
caryclark@google.com24bec792012-08-20 12:43:57 +00004522 Segment* undoneSegment(int& start, int& end) {
4523 int segmentCount = fSegments.count();
4524 for (int test = 0; test < segmentCount; ++test) {
4525 Segment* testSegment = &fSegments[test];
4526 if (testSegment->done()) {
4527 continue;
4528 }
4529 testSegment->undoneSpan(start, end);
4530 return testSegment;
4531 }
4532 return NULL;
4533 }
4534
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004535 int updateSegment(int index, const SkPoint* pts) {
4536 Segment& segment = fSegments[index];
4537 segment.updatePts(pts);
4538 return segment.verb() + 1;
4539 }
4540
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004541#if DEBUG_TEST
4542 SkTArray<Segment>& debugSegments() {
4543 return fSegments;
4544 }
4545#endif
4546
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004547#if DEBUG_DUMP
4548 void dump() {
4549 int i;
4550 const char className[] = "Contour";
4551 const int tab = 4;
4552 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
4553 for (i = 0; i < fSegments.count(); ++i) {
4554 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
4555 className, i);
4556 fSegments[i].dump();
4557 }
4558 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
4559 tab + sizeof(className), className,
4560 fBounds.fLeft, fBounds.fTop,
4561 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004562 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
4563 className, fContainsIntercepts);
4564 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
4565 className, fContainsCurves);
4566 }
4567#endif
4568
caryclark@google.com027de222012-07-12 12:52:50 +00004569#if DEBUG_ACTIVE_SPANS
4570 void debugShowActiveSpans() {
4571 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004572 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00004573 }
4574 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004575
4576 void validateActiveSpans() {
4577 for (int index = 0; index < fSegments.count(); ++index) {
4578 fSegments[index].validateActiveSpans();
4579 }
4580 }
caryclark@google.com027de222012-07-12 12:52:50 +00004581#endif
4582
caryclark@google.com729e1c42012-11-21 21:36:34 +00004583#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004584 int debugShowWindingValues(int totalSegments, int ofInterest) {
4585 int count = fSegments.count();
4586 int sum = 0;
4587 for (int index = 0; index < count; ++index) {
4588 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
4589 }
4590 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
4591 return sum;
4592 }
4593
4594 static void debugShowWindingValues(SkTDArray<Contour*>& contourList) {
4595 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
4596 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
4597 int ofInterest = 1 << 5 | 1 << 8;
4598 int total = 0;
4599 int index;
4600 for (index = 0; index < contourList.count(); ++index) {
4601 total += contourList[index]->segments().count();
4602 }
4603 int sum = 0;
4604 for (index = 0; index < contourList.count(); ++index) {
4605 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
4606 }
4607 // SkDebugf("%s total=%d\n", __FUNCTION__, sum);
4608 }
4609#endif
4610
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004611protected:
4612 void setBounds() {
4613 int count = fSegments.count();
4614 if (count == 0) {
4615 SkDebugf("%s empty contour\n", __FUNCTION__);
4616 SkASSERT(0);
4617 // FIXME: delete empty contour?
4618 return;
4619 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004620 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004621 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004622 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004623 }
4624 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004625
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004626private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004627 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004628 SkTDArray<Segment*> fSortedSegments;
4629 int fFirstSorted;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004630 SkTDArray<Coincidence> fCoincidences;
4631 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004632 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004633 bool fContainsIntercepts;
4634 bool fContainsCurves;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004635 bool fDone;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004636 bool fOperand; // true for the second argument to a binary operator
4637 bool fXor;
caryclark@google.com4eeda372012-12-06 21:47:48 +00004638 bool fOppXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004639#if DEBUG_DUMP
4640 int fID;
4641#endif
4642};
4643
4644class EdgeBuilder {
4645public:
4646
caryclark@google.comf839c032012-10-26 21:03:50 +00004647EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
4648 : fPath(path.nativePath())
4649 , fContours(contours)
4650{
4651 init();
4652}
4653
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004654EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00004655 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004656 , fContours(contours)
4657{
caryclark@google.comf839c032012-10-26 21:03:50 +00004658 init();
4659}
4660
4661void init() {
4662 fCurrentContour = NULL;
4663 fOperand = false;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004664 fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004665#if DEBUG_DUMP
4666 gContourID = 0;
4667 gSegmentID = 0;
4668#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00004669 fSecondHalf = preFetch();
4670}
4671
4672void addOperand(const SkPath& path) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004673 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
4674 fPathVerbs.pop();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004675 fPath = &path;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004676 fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004677 preFetch();
4678}
4679
4680void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004681 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004682 complete();
4683 if (fCurrentContour && !fCurrentContour->segments().count()) {
4684 fContours.pop_back();
4685 }
4686 // correct pointers in contours since fReducePts may have moved as it grew
4687 int cIndex = 0;
4688 int extraCount = fExtra.count();
4689 SkASSERT(extraCount == 0 || fExtra[0] == -1);
4690 int eIndex = 0;
4691 int rIndex = 0;
4692 while (++eIndex < extraCount) {
4693 int offset = fExtra[eIndex];
4694 if (offset < 0) {
4695 ++cIndex;
4696 continue;
4697 }
4698 fCurrentContour = &fContours[cIndex];
4699 rIndex += fCurrentContour->updateSegment(offset - 1,
4700 &fReducePts[rIndex]);
4701 }
4702 fExtra.reset(); // we're done with this
4703}
4704
4705ShapeOpMask xorMask() const {
caryclark@google.com729e1c42012-11-21 21:36:34 +00004706 return fXorMask[fOperand];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004707}
4708
4709protected:
4710
4711void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004712 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004713 fCurrentContour->complete();
4714 fCurrentContour = NULL;
4715 }
4716}
4717
caryclark@google.com235f56a2012-09-14 14:19:30 +00004718// FIXME:remove once we can access path pts directly
4719int preFetch() {
4720 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004721 SkPoint pts[4];
4722 SkPath::Verb verb;
4723 do {
4724 verb = iter.next(pts);
4725 *fPathVerbs.append() = verb;
4726 if (verb == SkPath::kMove_Verb) {
4727 *fPathPts.append() = pts[0];
4728 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
4729 fPathPts.append(verb, &pts[1]);
4730 }
4731 } while (verb != SkPath::kDone_Verb);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004732 return fPathVerbs.count() - 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004733}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004734
caryclark@google.com235f56a2012-09-14 14:19:30 +00004735void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004736 SkPath::Verb reducedVerb;
4737 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004738 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004739 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004740 const SkPoint* finalCurveStart = NULL;
4741 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004742 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004743 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
4744 switch (verb) {
4745 case SkPath::kMove_Verb:
4746 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004747 if (!fCurrentContour) {
4748 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004749 fCurrentContour->setOperand(fOperand);
caryclark@google.com729e1c42012-11-21 21:36:34 +00004750 fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004751 *fExtra.append() = -1; // start new contour
4752 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004753 finalCurveEnd = pointsPtr++;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004754 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004755 case SkPath::kLine_Verb:
4756 // skip degenerate points
4757 if (pointsPtr[-1].fX != pointsPtr[0].fX
4758 || pointsPtr[-1].fY != pointsPtr[0].fY) {
4759 fCurrentContour->addLine(&pointsPtr[-1]);
4760 }
4761 break;
4762 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00004763
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004764 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
4765 if (reducedVerb == 0) {
4766 break; // skip degenerate points
4767 }
4768 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00004769 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004770 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004771 break;
4772 }
4773 fCurrentContour->addQuad(&pointsPtr[-1]);
4774 break;
4775 case SkPath::kCubic_Verb:
4776 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
4777 if (reducedVerb == 0) {
4778 break; // skip degenerate points
4779 }
4780 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004781 *fExtra.append() =
4782 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004783 break;
4784 }
4785 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004786 *fExtra.append() =
4787 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004788 break;
4789 }
4790 fCurrentContour->addCubic(&pointsPtr[-1]);
4791 break;
4792 case SkPath::kClose_Verb:
4793 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004794 if (finalCurveStart && finalCurveEnd
4795 && *finalCurveStart != *finalCurveEnd) {
4796 *fReducePts.append() = *finalCurveStart;
4797 *fReducePts.append() = *finalCurveEnd;
4798 *fExtra.append() =
4799 fCurrentContour->addLine(fReducePts.end() - 2);
4800 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004801 complete();
caryclark@google.com31143cf2012-11-09 22:14:19 +00004802 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004803 default:
4804 SkDEBUGFAIL("bad verb");
4805 return;
4806 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004807 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004808 pointsPtr += verb;
4809 SkASSERT(fCurrentContour);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004810 nextVerb:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004811 if (verbPtr == endOfFirstHalf) {
4812 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004813 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004814 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004815}
4816
4817private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004818 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004819 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004820 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004821 Contour* fCurrentContour;
4822 SkTArray<Contour>& fContours;
4823 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004824 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com729e1c42012-11-21 21:36:34 +00004825 ShapeOpMask fXorMask[2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004826 int fSecondHalf;
4827 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004828};
4829
4830class Work {
4831public:
4832 enum SegmentType {
4833 kHorizontalLine_Segment = -1,
4834 kVerticalLine_Segment = 0,
4835 kLine_Segment = SkPath::kLine_Verb,
4836 kQuad_Segment = SkPath::kQuad_Verb,
4837 kCubic_Segment = SkPath::kCubic_Verb,
4838 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004839
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004840 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4841 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4842 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004843
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004844 // FIXME: does it make sense to write otherIndex now if we're going to
4845 // fix it up later?
4846 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004847 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004848 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004849
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004850 // Avoid collapsing t values that are close to the same since
4851 // we walk ts to describe consecutive intersections. Since a pair of ts can
4852 // be nearly equal, any problems caused by this should be taken care
4853 // of later.
4854 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004855 int addT(double newT, const Work& other) {
4856 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004857 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004858
caryclark@google.com73ca6242013-01-17 21:02:47 +00004859 int addUnsortableT(double newT, const Work& other, bool start) {
4860 return fContour->addUnsortableT(fIndex, newT, other.fContour, other.fIndex, start);
4861 }
4862
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004863 bool advance() {
4864 return ++fIndex < fLast;
4865 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004866
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004867 SkScalar bottom() const {
4868 return bounds().fBottom;
4869 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004870
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004871 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004872 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004873 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004874
caryclark@google.com73ca6242013-01-17 21:02:47 +00004875#if !APPROXIMATE_CUBICS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004876 const SkPoint* cubic() const {
4877 return fCubic;
4878 }
caryclark@google.com73ca6242013-01-17 21:02:47 +00004879#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004880
4881 void init(Contour* contour) {
4882 fContour = contour;
4883 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004884 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004885 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004886
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004887 bool isAdjacent(const Work& next) {
4888 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4889 }
4890
4891 bool isFirstLast(const Work& next) {
4892 return fContour == next.fContour && fIndex == 0
4893 && next.fIndex == fLast - 1;
4894 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004895
4896 SkScalar left() const {
4897 return bounds().fLeft;
4898 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004899
caryclark@google.com73ca6242013-01-17 21:02:47 +00004900#if !APPROXIMATE_CUBICS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004901 void promoteToCubic() {
4902 fCubic[0] = pts()[0];
4903 fCubic[2] = pts()[1];
4904 fCubic[3] = pts()[2];
4905 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4906 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4907 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4908 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4909 }
caryclark@google.com73ca6242013-01-17 21:02:47 +00004910#endif
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004911
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004912 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004913 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004914 }
4915
4916 SkScalar right() const {
4917 return bounds().fRight;
4918 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004919
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004920 ptrdiff_t segmentIndex() const {
4921 return fIndex;
4922 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004923
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004924 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004925 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004926 SegmentType type = (SegmentType) segment.verb();
4927 if (type != kLine_Segment) {
4928 return type;
4929 }
4930 if (segment.isHorizontal()) {
4931 return kHorizontalLine_Segment;
4932 }
4933 if (segment.isVertical()) {
4934 return kVerticalLine_Segment;
4935 }
4936 return kLine_Segment;
4937 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004938
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004939 bool startAfter(const Work& after) {
4940 fIndex = after.fIndex;
4941 return advance();
4942 }
4943
4944 SkScalar top() const {
4945 return bounds().fTop;
4946 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004947
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004948 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004949 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004950 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004951
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004952 SkScalar x() const {
4953 return bounds().fLeft;
4954 }
4955
4956 bool xFlipped() const {
4957 return x() != pts()[0].fX;
4958 }
4959
4960 SkScalar y() const {
4961 return bounds().fTop;
4962 }
4963
4964 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004965 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004966 }
4967
4968protected:
4969 Contour* fContour;
caryclark@google.com73ca6242013-01-17 21:02:47 +00004970#if !APPROXIMATE_CUBICS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004971 SkPoint fCubic[4];
caryclark@google.com73ca6242013-01-17 21:02:47 +00004972#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004973 int fIndex;
4974 int fLast;
4975};
4976
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004977#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004978static void debugShowLineIntersection(int pts, const Work& wt,
4979 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004980 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004981 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004982 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4983 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4984 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4985 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004986 return;
4987 }
4988 SkPoint wtOutPt, wnOutPt;
4989 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4990 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004991 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 +00004992 __FUNCTION__,
4993 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4994 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4995 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004996 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004997 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004998 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004999 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
5000 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
5001 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00005002 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
5003 }
5004 SkDebugf("\n");
5005}
5006
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005007static void debugShowQuadLineIntersection(int pts, const Work& wt,
5008 const Work& wn, const double wtTs[2], const double wnTs[2]) {
5009 if (!pts) {
5010 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00005011 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005012 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
5013 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005014 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005015 return;
5016 }
5017 SkPoint wtOutPt, wnOutPt;
5018 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
5019 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
5020 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5021 __FUNCTION__,
5022 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
5023 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
5024 wtOutPt.fX, wtOutPt.fY);
5025 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005026 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
5027 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005028 }
5029 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5030 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
5031 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
5032 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005033 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
5034 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005035 }
5036 SkDebugf("\n");
5037}
5038
caryclark@google.com73ca6242013-01-17 21:02:47 +00005039// FIXME: show more than two intersection points
caryclark@google.coma461ff02012-10-11 12:54:23 +00005040static void debugShowQuadIntersection(int pts, const Work& wt,
5041 const Work& wn, const double wtTs[2], const double wnTs[2]) {
5042 if (!pts) {
5043 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
5044 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
5045 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00005046 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00005047 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005048 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00005049 return;
5050 }
5051 SkPoint wtOutPt, wnOutPt;
5052 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
5053 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
5054 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5055 __FUNCTION__,
5056 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
5057 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
5058 wtOutPt.fX, wtOutPt.fY);
5059 if (pts == 2) {
5060 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
5061 }
5062 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5063 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
5064 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
5065 wnOutPt.fX, wnOutPt.fY);
5066 if (pts == 2) {
5067 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005068 }
caryclark@google.comb9738012012-07-03 19:53:30 +00005069 SkDebugf("\n");
5070}
caryclark@google.com73ca6242013-01-17 21:02:47 +00005071
5072static void debugShowCubicLineIntersection(int pts, const Work& wt,
5073 const Work& wn, const double wtTs[2], const double wnTs[2]) {
5074 if (!pts) {
5075 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
5076 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
5077 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY,
5078 wt.pts()[2].fX, wt.pts()[2].fY, wt.pts()[3].fX, wt.pts()[3].fY,
5079 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
5080 return;
5081 }
5082 SkPoint wtOutPt, wnOutPt;
5083 CubicXYAtT(wt.pts(), wtTs[0], &wtOutPt);
5084 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
5085 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5086 __FUNCTION__,
5087 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY,
5088 wt.pts()[2].fX, wt.pts()[2].fY, wt.pts()[3].fX, wt.pts()[3].fY,
5089 wtOutPt.fX, wtOutPt.fY);
5090 if (pts == 2) {
5091 CubicXYAtT(wt.pts(), wtTs[1], &wtOutPt);
5092 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
5093 }
5094 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5095 wtTs[0], wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
5096 wnOutPt.fX, wnOutPt.fY);
5097 if (pts == 2) {
5098 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
5099 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
5100 }
5101 SkDebugf("\n");
5102}
5103
5104#if APPROXIMATE_CUBICS
5105// FIXME: show more than two intersection points
5106static void debugShowCubicQuadIntersection(int pts, const Work& wt,
5107 const Work& wn, const double wtTs[2], const double wnTs[2]) {
5108 if (!pts) {
5109 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
5110 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
5111 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY,
5112 wt.pts()[2].fX, wt.pts()[2].fY, wt.pts()[3].fX, wt.pts()[3].fY,
5113 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
5114 wn.pts()[2].fX, wn.pts()[2].fY );
5115 return;
5116 }
5117 SkPoint wtOutPt, wnOutPt;
5118 CubicXYAtT(wt.pts(), wtTs[0], &wtOutPt);
5119 CubicXYAtT(wn.pts(), wnTs[0], &wnOutPt);
5120 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5121 __FUNCTION__,
5122 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY,
5123 wt.pts()[2].fX, wt.pts()[2].fY, wt.pts()[3].fX, wt.pts()[3].fY,
5124 wtOutPt.fX, wtOutPt.fY);
5125 if (pts == 2) {
5126 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
5127 }
5128 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5129 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
5130 wn.pts()[2].fX, wn.pts()[2].fY,
5131 wnOutPt.fX, wnOutPt.fY);
5132 if (pts == 2) {
5133 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
5134 }
5135 SkDebugf("\n");
5136}
5137
5138// FIXME: show more than two intersection points
5139static void debugShowCubicIntersection(int pts, const Work& wt,
5140 const Work& wn, const double wtTs[2], const double wnTs[2]) {
5141 if (!pts) {
5142 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
5143 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
5144 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY,
5145 wt.pts()[2].fX, wt.pts()[2].fY, wt.pts()[3].fX, wt.pts()[3].fY,
5146 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
5147 wn.pts()[2].fX, wn.pts()[2].fY, wn.pts()[3].fX, wn.pts()[3].fY );
5148 return;
5149 }
5150 SkPoint wtOutPt, wnOutPt;
5151 CubicXYAtT(wt.pts(), wtTs[0], &wtOutPt);
5152 CubicXYAtT(wn.pts(), wnTs[0], &wnOutPt);
5153 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5154 __FUNCTION__,
5155 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY,
5156 wt.pts()[2].fX, wt.pts()[2].fY, wt.pts()[3].fX, wt.pts()[3].fY,
5157 wtOutPt.fX, wtOutPt.fY);
5158 if (pts == 2) {
5159 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
5160 }
5161 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5162 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
5163 wn.pts()[2].fX, wn.pts()[2].fY, wn.pts()[3].fX, wn.pts()[3].fY,
5164 wnOutPt.fX, wnOutPt.fY);
5165 if (pts == 2) {
5166 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
5167 }
5168 SkDebugf("\n");
5169}
5170#endif
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005171#else
5172static void debugShowLineIntersection(int , const Work& ,
5173 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005174}
caryclark@google.coma461ff02012-10-11 12:54:23 +00005175
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005176static void debugShowQuadLineIntersection(int , const Work& ,
5177 const Work& , const double [2], const double [2]) {
5178}
5179
caryclark@google.coma461ff02012-10-11 12:54:23 +00005180static void debugShowQuadIntersection(int , const Work& ,
5181 const Work& , const double [2], const double [2]) {
5182}
caryclark@google.com73ca6242013-01-17 21:02:47 +00005183
5184static void debugShowCubicLineIntersection(int , const Work& ,
5185 const Work& , const double [2], const double [2]) {
5186}
5187
5188#if APPROXIMATE_CUBICS
5189static void debugShowCubicQuadIntersection(int , const Work& ,
5190 const Work& , const double [2], const double [2]) {
5191}
5192
5193static void debugShowCubicIntersection(int , const Work& ,
5194 const Work& , const double [2], const double [2]) {
5195}
5196#endif
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005197#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005198
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005199static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005200
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005201 if (test != next) {
5202 if (test->bounds().fBottom < next->bounds().fTop) {
5203 return false;
5204 }
5205 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
5206 return true;
5207 }
5208 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005209 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005210 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005211 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005212 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005213 Work wn;
5214 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005215 if (test == next && !wn.startAfter(wt)) {
5216 continue;
5217 }
5218 do {
5219 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
5220 continue;
5221 }
5222 int pts;
5223 Intersections ts;
5224 bool swap = false;
5225 switch (wt.segmentType()) {
5226 case Work::kHorizontalLine_Segment:
5227 swap = true;
5228 switch (wn.segmentType()) {
5229 case Work::kHorizontalLine_Segment:
5230 case Work::kVerticalLine_Segment:
5231 case Work::kLine_Segment: {
5232 pts = HLineIntersect(wn.pts(), wt.left(),
5233 wt.right(), wt.y(), wt.xFlipped(), 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 }
5238 case Work::kQuad_Segment: {
5239 pts = HQuadIntersect(wn.pts(), wt.left(),
5240 wt.right(), wt.y(), wt.xFlipped(), ts);
5241 break;
5242 }
5243 case Work::kCubic_Segment: {
5244 pts = HCubicIntersect(wn.pts(), wt.left(),
5245 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.com73ca6242013-01-17 21:02:47 +00005246 debugShowCubicLineIntersection(pts, wn, wt, ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005247 break;
5248 }
5249 default:
5250 SkASSERT(0);
5251 }
5252 break;
5253 case Work::kVerticalLine_Segment:
5254 swap = true;
5255 switch (wn.segmentType()) {
5256 case Work::kHorizontalLine_Segment:
5257 case Work::kVerticalLine_Segment:
5258 case Work::kLine_Segment: {
5259 pts = VLineIntersect(wn.pts(), wt.top(),
5260 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005261 debugShowLineIntersection(pts, wt, wn,
5262 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005263 break;
5264 }
5265 case Work::kQuad_Segment: {
5266 pts = VQuadIntersect(wn.pts(), wt.top(),
5267 wt.bottom(), wt.x(), wt.yFlipped(), ts);
5268 break;
5269 }
5270 case Work::kCubic_Segment: {
5271 pts = VCubicIntersect(wn.pts(), wt.top(),
5272 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.com73ca6242013-01-17 21:02:47 +00005273 debugShowCubicLineIntersection(pts, wn, wt, ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005274 break;
5275 }
5276 default:
5277 SkASSERT(0);
5278 }
5279 break;
5280 case Work::kLine_Segment:
5281 switch (wn.segmentType()) {
5282 case Work::kHorizontalLine_Segment:
5283 pts = HLineIntersect(wt.pts(), wn.left(),
5284 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005285 debugShowLineIntersection(pts, wt, wn,
5286 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005287 break;
5288 case Work::kVerticalLine_Segment:
5289 pts = VLineIntersect(wt.pts(), wn.top(),
5290 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005291 debugShowLineIntersection(pts, wt, wn,
5292 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005293 break;
5294 case Work::kLine_Segment: {
5295 pts = LineIntersect(wt.pts(), wn.pts(), ts);
5296 debugShowLineIntersection(pts, wt, wn,
5297 ts.fT[1], ts.fT[0]);
5298 break;
5299 }
5300 case Work::kQuad_Segment: {
5301 swap = true;
5302 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005303 debugShowQuadLineIntersection(pts, wn, wt,
5304 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005305 break;
5306 }
5307 case Work::kCubic_Segment: {
5308 swap = true;
5309 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com73ca6242013-01-17 21:02:47 +00005310 debugShowCubicLineIntersection(pts, wn, wt, ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005311 break;
5312 }
5313 default:
5314 SkASSERT(0);
5315 }
5316 break;
5317 case Work::kQuad_Segment:
5318 switch (wn.segmentType()) {
5319 case Work::kHorizontalLine_Segment:
5320 pts = HQuadIntersect(wt.pts(), wn.left(),
5321 wn.right(), wn.y(), wn.xFlipped(), ts);
5322 break;
5323 case Work::kVerticalLine_Segment:
5324 pts = VQuadIntersect(wt.pts(), wn.top(),
5325 wn.bottom(), wn.x(), wn.yFlipped(), ts);
5326 break;
5327 case Work::kLine_Segment: {
5328 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005329 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005330 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005331 break;
5332 }
5333 case Work::kQuad_Segment: {
5334 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00005335 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005336 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005337 break;
5338 }
5339 case Work::kCubic_Segment: {
caryclark@google.com73ca6242013-01-17 21:02:47 +00005340 #if APPROXIMATE_CUBICS
5341 swap = true;
5342 pts = CubicQuadIntersect(wn.pts(), wt.pts(), ts);
5343 debugShowCubicQuadIntersection(pts, wn, wt, ts.fT[0], ts.fT[1]);
5344 #else
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005345 wt.promoteToCubic();
5346 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
caryclark@google.com73ca6242013-01-17 21:02:47 +00005347 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005348 break;
5349 }
5350 default:
5351 SkASSERT(0);
5352 }
5353 break;
5354 case Work::kCubic_Segment:
5355 switch (wn.segmentType()) {
5356 case Work::kHorizontalLine_Segment:
5357 pts = HCubicIntersect(wt.pts(), wn.left(),
5358 wn.right(), wn.y(), wn.xFlipped(), ts);
5359 break;
5360 case Work::kVerticalLine_Segment:
5361 pts = VCubicIntersect(wt.pts(), wn.top(),
5362 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.com73ca6242013-01-17 21:02:47 +00005363 debugShowCubicLineIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005364 break;
5365 case Work::kLine_Segment: {
5366 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.com73ca6242013-01-17 21:02:47 +00005367 debugShowCubicLineIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005368 break;
5369 }
5370 case Work::kQuad_Segment: {
caryclark@google.com73ca6242013-01-17 21:02:47 +00005371 #if APPROXIMATE_CUBICS
5372 pts = CubicQuadIntersect(wt.pts(), wn.pts(), ts);
5373 debugShowCubicQuadIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]);
5374 #else
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005375 wn.promoteToCubic();
5376 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
caryclark@google.com73ca6242013-01-17 21:02:47 +00005377 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005378 break;
5379 }
5380 case Work::kCubic_Segment: {
caryclark@google.com73ca6242013-01-17 21:02:47 +00005381 #if APPROXIMATE_CUBICS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005382 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.com73ca6242013-01-17 21:02:47 +00005383 debugShowCubicIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]);
5384 #else
5385 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
5386 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005387 break;
5388 }
5389 default:
5390 SkASSERT(0);
5391 }
5392 break;
5393 default:
5394 SkASSERT(0);
5395 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005396 if (!foundCommonContour && pts > 0) {
5397 test->addCross(next);
5398 next->addCross(test);
5399 foundCommonContour = true;
5400 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005401 // in addition to recording T values, record matching segment
caryclark@google.com73ca6242013-01-17 21:02:47 +00005402 if (ts.unsortable()) {
5403 bool start = true;
5404 for (int pt = 0; pt < ts.used(); ++pt) {
5405 // FIXME: if unsortable, the other points to the original. This logic is
5406 // untested downstream.
5407 int testTAt = wt.addUnsortableT(ts.fT[swap][pt], wt, start);
5408 wt.addOtherT(testTAt, ts.fT[swap][pt], testTAt);
5409 testTAt = wn.addUnsortableT(ts.fT[!swap][pt], wn, start ^ ts.fFlip);
5410 wn.addOtherT(testTAt, ts.fT[!swap][pt], testTAt);
5411 start ^= true;
5412 }
5413 continue;
5414 }
caryclark@google.com32546db2012-08-31 20:55:07 +00005415 if (pts == 2) {
5416 if (wn.segmentType() <= Work::kLine_Segment
5417 && wt.segmentType() <= Work::kLine_Segment) {
5418 wt.addCoincident(wn, ts, swap);
5419 continue;
5420 }
5421 if (wn.segmentType() == Work::kQuad_Segment
5422 && wt.segmentType() == Work::kQuad_Segment
5423 && ts.coincidentUsed() == 2) {
5424 wt.addCoincident(wn, ts, swap);
5425 continue;
5426 }
5427
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00005428 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00005429 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005430 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
5431 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005432 int testTAt = wt.addT(ts.fT[swap][pt], wn);
5433 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005434 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
5435 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005436 }
5437 } while (wn.advance());
5438 } while (wt.advance());
5439 return true;
5440}
5441
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005442// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005443// see if coincidence is formed by clipping non-concident segments
caryclark@google.com4eeda372012-12-06 21:47:48 +00005444static void coincidenceCheck(SkTDArray<Contour*>& contourList, int total) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005445 int contourCount = contourList.count();
caryclark@google.com8f9f4682013-01-03 21:18:16 +00005446#if ONE_PASS_COINCIDENCE_CHECK
caryclark@google.comf25edfe2012-06-01 18:20:10 +00005447 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005448 Contour* contour = contourList[cIndex];
caryclark@google.com7ba591e2012-11-20 14:21:54 +00005449 contour->resolveCoincidence(contourList);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005450 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +00005451#else
5452 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5453 Contour* contour = contourList[cIndex];
5454 contour->addCoincidentPoints();
5455 }
5456 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5457 Contour* contour = contourList[cIndex];
5458 contour->calcCoincidentWinding();
5459 }
5460#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005461 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5462 Contour* contour = contourList[cIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00005463 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005464 }
5465}
5466
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005467static int contourRangeCheckY(SkTDArray<Contour*>& contourList, Segment*& current, int& index,
caryclark@google.com3586ece2012-12-27 18:46:58 +00005468 int& endIndex, double& bestHit, SkScalar& bestDx, bool& tryAgain, double& mid, bool opp) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005469 SkPoint basePt;
caryclark@google.com10227bf2012-12-28 22:10:41 +00005470 double tAtMid = current->tAtMid(index, endIndex, mid);
5471 current->xyAtT(tAtMid, basePt);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005472 int contourCount = contourList.count();
5473 SkScalar bestY = SK_ScalarMin;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005474 Segment* bestSeg = NULL;
5475 int bestTIndex;
5476 bool bestOpp;
caryclark@google.com3586ece2012-12-27 18:46:58 +00005477 bool hitSomething = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005478 for (int cTest = 0; cTest < contourCount; ++cTest) {
5479 Contour* contour = contourList[cTest];
5480 bool testOpp = contour->operand() ^ current->operand() ^ opp;
5481 if (basePt.fY < contour->bounds().fTop) {
5482 continue;
5483 }
5484 if (bestY > contour->bounds().fBottom) {
5485 continue;
5486 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005487 int segmentCount = contour->segments().count();
5488 for (int test = 0; test < segmentCount; ++test) {
5489 Segment* testSeg = &contour->segments()[test];
5490 SkScalar testY = bestY;
5491 double testHit;
caryclark@google.com10227bf2012-12-28 22:10:41 +00005492 int testTIndex = testSeg->crossedSpanY(basePt, testY, testHit, hitSomething, tAtMid,
5493 testOpp, testSeg == current);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005494 if (testTIndex < 0) {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00005495 if (testTIndex == SK_MinS32) {
5496 hitSomething = true;
5497 bestSeg = NULL;
5498 goto abortContours; // vertical encountered, return and try different point
5499 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005500 continue;
5501 }
5502 if (testSeg == current && current->betweenTs(index, testHit, endIndex)) {
caryclark@google.com3586ece2012-12-27 18:46:58 +00005503 double baseT = current->t(index);
5504 double endT = current->t(endIndex);
5505 double newMid = (testHit - baseT) / (endT - baseT);
5506#if DEBUG_WINDING
5507 SkPoint midXY, newXY;
caryclark@google.com10227bf2012-12-28 22:10:41 +00005508 double midT = current->tAtMid(index, endIndex, mid);
5509 current->xyAtT(midT, midXY);
5510 double newMidT = current->tAtMid(index, endIndex, newMid);
5511 current->xyAtT(newMidT, newXY);
caryclark@google.com3586ece2012-12-27 18:46:58 +00005512 SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)"
5513 " n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__,
5514 current->debugID(), mid, newMid,
5515 baseT, current->xAtT(index), current->yAtT(index),
5516 baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
5517 baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
5518 endT, current->xAtT(endIndex), current->yAtT(endIndex));
5519#endif
5520 mid = newMid * 2; // calling loop with divide by 2 before continuing
5521 return SK_MinS32;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005522 }
5523 bestSeg = testSeg;
5524 bestHit = testHit;
5525 bestOpp = testOpp;
5526 bestTIndex = testTIndex;
5527 bestY = testY;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005528 }
5529 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +00005530abortContours:
caryclark@google.com10227bf2012-12-28 22:10:41 +00005531 int result;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005532 if (!bestSeg) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00005533 result = hitSomething ? SK_MinS32 : 0;
5534 } else {
5535 if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
5536 current = bestSeg;
5537 index = bestTIndex;
5538 endIndex = bestSeg->nextSpan(bestTIndex, 1);
5539 SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
5540 tryAgain = true;
5541 return 0;
5542 }
5543 result = bestSeg->windingAtT(bestHit, bestTIndex, bestOpp, bestDx);
5544 SkASSERT(bestDx);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005545 }
caryclark@google.com3586ece2012-12-27 18:46:58 +00005546 double baseT = current->t(index);
5547 double endT = current->t(endIndex);
5548 bestHit = baseT + mid * (endT - baseT);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005549 return result;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005550}
5551
caryclark@google.com24bec792012-08-20 12:43:57 +00005552static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
5553 int contourCount = contourList.count();
5554 Segment* result;
5555 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5556 Contour* contour = contourList[cIndex];
5557 result = contour->undoneSegment(start, end);
5558 if (result) {
5559 return result;
5560 }
5561 }
5562 return NULL;
5563}
5564
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005565#define OLD_FIND_CHASE 1
caryclark@google.com24bec792012-08-20 12:43:57 +00005566
caryclark@google.com31143cf2012-11-09 22:14:19 +00005567static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005568 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005569 Span* span;
5570 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005571 const Span& backPtr = span->fOther->span(span->fOtherIndex);
5572 Segment* segment = backPtr.fOther;
5573 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005574 SkTDArray<Angle> angles;
5575 int done = 0;
5576 if (segment->activeAngle(tIndex, done, angles)) {
5577 Angle* last = angles.end() - 1;
5578 tIndex = last->start();
5579 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005580 #if TRY_ROTATE
5581 *chase.insert(0) = span;
5582 #else
5583 *chase.append() = span;
5584 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005585 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005586 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005587 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00005588 continue;
5589 }
5590 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005591 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005592 int angleCount = sorted.count();
caryclark@google.com03f97062012-08-21 13:13:52 +00005593#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005594 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00005595#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005596 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005597 continue;
5598 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005599 // find first angle, initialize winding to computed fWindSum
5600 int firstIndex = -1;
5601 const Angle* angle;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005602#if OLD_FIND_CHASE
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005603 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005604 do {
5605 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005606 segment = angle->segment();
5607 winding = segment->windSum(angle);
5608 } while (winding == SK_MinS32);
5609 int spanWinding = segment->spanSign(angle->start(), angle->end());
5610 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005611 SkDebugf("%s winding=%d spanWinding=%d\n",
5612 __FUNCTION__, winding, spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00005613 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005614 // turn span winding into contour winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005615 if (spanWinding * winding < 0) {
5616 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005617 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005618 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005619 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005620 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005621 // we care about first sign and whether wind sum indicates this
5622 // edge is inside or outside. Maybe need to pass span winding
5623 // or first winding or something into this function?
5624 // advance to first undone angle, then return it and winding
5625 // (to set whether edges are active or not)
5626 int nextIndex = firstIndex + 1;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005627 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005628 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00005629 winding -= angle->segment()->spanSign(angle);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005630#else
5631 do {
5632 angle = sorted[++firstIndex];
5633 segment = angle->segment();
5634 } while (segment->windSum(angle) == SK_MinS32);
5635 #if DEBUG_SORT
5636 segment->debugShowSort(__FUNCTION__, sorted, firstIndex);
5637 #endif
5638 int sumWinding = segment->updateWindingReverse(angle);
5639 int nextIndex = firstIndex + 1;
5640 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
5641 Segment* first = NULL;
5642#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005643 do {
5644 SkASSERT(nextIndex != firstIndex);
5645 if (nextIndex == angleCount) {
5646 nextIndex = 0;
5647 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005648 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00005649 segment = angle->segment();
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005650#if OLD_FIND_CHASE
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005651 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00005652 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005653 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00005654 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
5655 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005656 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005657 tIndex = angle->start();
5658 endIndex = angle->end();
5659 int lesser = SkMin32(tIndex, endIndex);
5660 const Span& nextSpan = segment->span(lesser);
5661 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005662#if 1
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005663 // FIXME: this be wrong? assign startWinding if edge is in
caryclark@google.com9764cc62012-07-12 19:29:45 +00005664 // same direction. If the direction is opposite, winding to
5665 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00005666 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005667 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005668 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005669 segment->markAndChaseWinding(angle, maxWinding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005670#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005671 break;
5672 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005673#else
5674 int start = angle->start();
5675 int end = angle->end();
5676 int maxWinding;
5677 segment->setUpWinding(start, end, maxWinding, sumWinding);
5678 if (!segment->done(angle)) {
5679 if (!first) {
5680 first = segment;
5681 tIndex = start;
5682 endIndex = end;
5683 }
5684 (void) segment->markAngle(maxWinding, sumWinding, true, angle);
5685 }
5686#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005687 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005688 #if TRY_ROTATE
5689 *chase.insert(0) = span;
5690 #else
5691 *chase.append() = span;
5692 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005693 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005694 }
5695 return NULL;
5696}
5697
caryclark@google.com027de222012-07-12 12:52:50 +00005698#if DEBUG_ACTIVE_SPANS
5699static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005700 int index;
5701 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00005702 contourList[index]->debugShowActiveSpans();
5703 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005704 for (index = 0; index < contourList.count(); ++ index) {
5705 contourList[index]->validateActiveSpans();
5706 }
caryclark@google.com027de222012-07-12 12:52:50 +00005707}
5708#endif
5709
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005710static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005711 int& endIndex, SkPoint& topLeft, bool& unsortable, bool& done, bool onlySortable) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005712 Segment* result;
5713 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00005714 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005715 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00005716 Segment* topStart = NULL;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005717 done = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00005718 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5719 Contour* contour = contourList[cIndex];
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005720 if (contour->done()) {
5721 continue;
5722 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005723 const Bounds& bounds = contour->bounds();
5724 if (bounds.fBottom < topLeft.fY) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005725 done = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005726 continue;
5727 }
5728 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005729 done = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005730 continue;
5731 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005732 contour->topSortableSegment(topLeft, bestXY, topStart);
5733 if (!contour->done()) {
5734 done = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005735 }
5736 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005737 if (!topStart) {
5738 return NULL;
5739 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005740 topLeft = bestXY;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005741 result = topStart->findTop(index, endIndex, unsortable, onlySortable);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005742 } while (!result);
5743 return result;
5744}
caryclark@google.com31143cf2012-11-09 22:14:19 +00005745
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005746static int rightAngleWinding(SkTDArray<Contour*>& contourList,
caryclark@google.com3586ece2012-12-27 18:46:58 +00005747 Segment*& current, int& index, int& endIndex, double& tHit, SkScalar& hitDx, bool& tryAgain,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005748 bool opp) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005749 double test = 0.9;
5750 int contourWinding;
5751 do {
caryclark@google.com3586ece2012-12-27 18:46:58 +00005752 contourWinding = contourRangeCheckY(contourList, current, index, endIndex, tHit, hitDx,
5753 tryAgain, test, opp);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005754 if (contourWinding != SK_MinS32 || tryAgain) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005755 return contourWinding;
5756 }
5757 test /= 2;
5758 } while (!approximately_negative(test));
5759 SkASSERT(0); // should be OK to comment out, but interested when this hits
5760 return contourWinding;
5761}
5762
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005763static void skipVertical(SkTDArray<Contour*>& contourList,
5764 Segment*& current, int& index, int& endIndex) {
5765 if (!current->isVertical(index, endIndex)) {
5766 return;
5767 }
5768 int contourCount = contourList.count();
5769 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5770 Contour* contour = contourList[cIndex];
5771 if (contour->done()) {
5772 continue;
5773 }
5774 current = contour->nonVerticalSegment(index, endIndex);
5775 if (current) {
5776 return;
5777 }
5778 }
5779}
5780
caryclark@google.com3586ece2012-12-27 18:46:58 +00005781static Segment* findSortableTop(SkTDArray<Contour*>& contourList, bool& firstContour, int& index,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005782 int& endIndex, SkPoint& topLeft, bool& unsortable, bool& done, bool binary) {
5783 Segment* current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, done,
5784 true);
5785 if (!current) {
5786 return NULL;
5787 }
5788 if (firstContour) {
caryclark@google.com3586ece2012-12-27 18:46:58 +00005789 current->initWinding(index, endIndex);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005790 firstContour = false;
5791 return current;
5792 }
5793 int minIndex = SkMin32(index, endIndex);
5794 int sumWinding = current->windSum(minIndex);
5795 if (sumWinding != SK_MinS32) {
5796 return current;
5797 }
5798 sumWinding = current->computeSum(index, endIndex, binary);
5799 if (sumWinding != SK_MinS32) {
5800 return current;
5801 }
5802 int contourWinding;
5803 int oppContourWinding = 0;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005804 // the simple upward projection of the unresolved points hit unsortable angles
5805 // shoot rays at right angles to the segment to find its winding, ignoring angle cases
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005806 bool tryAgain;
5807 double tHit;
caryclark@google.com3586ece2012-12-27 18:46:58 +00005808 SkScalar hitDx = 0;
5809 SkScalar hitOppDx = 0;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005810 do {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005811 // if current is vertical, find another candidate which is not
5812 // if only remaining candidates are vertical, then they can be marked done
caryclark@google.com10227bf2012-12-28 22:10:41 +00005813 SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005814 skipVertical(contourList, current, index, endIndex);
caryclark@google.com10227bf2012-12-28 22:10:41 +00005815 SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005816 tryAgain = false;
caryclark@google.com3586ece2012-12-27 18:46:58 +00005817 contourWinding = rightAngleWinding(contourList, current, index, endIndex, tHit, hitDx,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005818 tryAgain, false);
5819 if (tryAgain) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005820 continue;
5821 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005822 if (!binary) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005823 break;
5824 }
caryclark@google.com3586ece2012-12-27 18:46:58 +00005825 oppContourWinding = rightAngleWinding(contourList, current, index, endIndex, tHit, hitOppDx,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005826 tryAgain, true);
5827 } while (tryAgain);
skia.committer@gmail.com8d83d0d2012-12-28 02:01:18 +00005828
caryclark@google.com3586ece2012-12-27 18:46:58 +00005829 current->initWinding(index, endIndex, tHit, contourWinding, hitDx, oppContourWinding, hitOppDx);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005830 return current;
5831}
5832
5833// rewrite that abandons keeping local track of winding
caryclark@google.com3586ece2012-12-27 18:46:58 +00005834static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005835 bool firstContour = true;
5836 bool unsortable = false;
5837 bool topUnsortable = false;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005838 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
5839 do {
5840 int index, endIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005841 bool topDone;
caryclark@google.com3586ece2012-12-27 18:46:58 +00005842 Segment* current = findSortableTop(contourList, firstContour, index, endIndex, topLeft,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005843 topUnsortable, topDone, false);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005844 if (!current) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005845 if (topUnsortable || !topDone) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005846 topUnsortable = false;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005847 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005848 topLeft.fX = topLeft.fY = SK_ScalarMin;
5849 continue;
5850 }
5851 break;
5852 }
5853 SkTDArray<Span*> chaseArray;
5854 do {
5855 if (current->activeWinding(index, endIndex)) {
5856 do {
5857 #if DEBUG_ACTIVE_SPANS
5858 if (!unsortable && current->done()) {
5859 debugShowActiveSpans(contourList);
5860 }
5861 #endif
5862 SkASSERT(unsortable || !current->done());
5863 int nextStart = index;
5864 int nextEnd = endIndex;
5865 Segment* next = current->findNextWinding(chaseArray, nextStart, nextEnd,
5866 unsortable);
5867 if (!next) {
5868 if (!unsortable && simple.hasMove()
5869 && current->verb() != SkPath::kLine_Verb
5870 && !simple.isClosed()) {
5871 current->addCurveTo(index, endIndex, simple, true);
5872 SkASSERT(simple.isClosed());
5873 }
5874 break;
5875 }
caryclark@google.com8f9f4682013-01-03 21:18:16 +00005876 #if DEBUG_FLOW
5877 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
5878 current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
5879 current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
5880 #endif
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005881 current->addCurveTo(index, endIndex, simple, true);
5882 current = next;
5883 index = nextStart;
5884 endIndex = nextEnd;
caryclark@google.com8f9f4682013-01-03 21:18:16 +00005885 } while (!simple.isClosed() && (!unsortable
caryclark@google.com10227bf2012-12-28 22:10:41 +00005886 || !current->done(SkMin32(index, endIndex))));
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005887 if (current->activeWinding(index, endIndex) && !simple.isClosed()) {
5888 SkASSERT(unsortable);
5889 int min = SkMin32(index, endIndex);
5890 if (!current->done(min)) {
5891 current->addCurveTo(index, endIndex, simple, true);
5892 current->markDoneUnary(min);
5893 }
5894 }
5895 simple.close();
5896 } else {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005897 Span* last = current->markAndChaseDoneUnary(index, endIndex);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005898 if (last) {
5899 *chaseArray.append() = last;
5900 }
5901 }
5902 current = findChase(chaseArray, index, endIndex);
5903 #if DEBUG_ACTIVE_SPANS
5904 debugShowActiveSpans(contourList);
5905 #endif
5906 if (!current) {
5907 break;
5908 }
5909 } while (true);
5910 } while (true);
5911 return simple.someAssemblyRequired();
5912}
5913
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005914// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005915static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005916 Segment* current;
5917 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005918 bool unsortable = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005919 bool closable = true;
caryclark@google.com24bec792012-08-20 12:43:57 +00005920 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005921 do {
caryclark@google.com8f9f4682013-01-03 21:18:16 +00005922 #if DEBUG_ACTIVE_SPANS
5923 if (!unsortable && current->done()) {
5924 debugShowActiveSpans(contourList);
5925 }
5926 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005927 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005928 int nextStart = start;
5929 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005930 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00005931 if (!next) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005932 if (!unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00005933 && current->verb() != SkPath::kLine_Verb
5934 && !simple.isClosed()) {
5935 current->addCurveTo(start, end, simple, true);
5936 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005937 }
caryclark@google.com24bec792012-08-20 12:43:57 +00005938 break;
5939 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005940 #if DEBUG_FLOW
5941 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
5942 current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
5943 current->xyAtT(end).fX, current->xyAtT(end).fY);
5944 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00005945 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00005946 current = next;
5947 start = nextStart;
5948 end = nextEnd;
caryclark@google.com8f9f4682013-01-03 21:18:16 +00005949 } while (!simple.isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005950 if (!simple.isClosed()) {
5951 SkASSERT(unsortable);
5952 int min = SkMin32(start, end);
5953 if (!current->done(min)) {
5954 current->addCurveTo(start, end, simple, true);
5955 current->markDone(min, 1);
5956 }
5957 closable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00005958 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005959 simple.close();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005960 #if DEBUG_ACTIVE_SPANS
5961 debugShowActiveSpans(contourList);
5962 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005963 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005964 return closable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005965}
5966
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005967static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5968 int contourCount = contourList.count();
5969 for (int cTest = 0; cTest < contourCount; ++cTest) {
5970 Contour* contour = contourList[cTest];
5971 contour->fixOtherTIndex();
5972 }
5973}
5974
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005975static void sortSegments(SkTDArray<Contour*>& contourList) {
5976 int contourCount = contourList.count();
5977 for (int cTest = 0; cTest < contourCount; ++cTest) {
5978 Contour* contour = contourList[cTest];
5979 contour->sortSegments();
5980 }
5981}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005982
caryclark@google.com4eeda372012-12-06 21:47:48 +00005983static void makeContourList(SkTArray<Contour>& contours, SkTDArray<Contour*>& list,
5984 bool evenOdd, bool oppEvenOdd) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005985 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005986 if (count == 0) {
5987 return;
5988 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005989 for (int index = 0; index < count; ++index) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00005990 Contour& contour = contours[index];
5991 contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
5992 *list.append() = &contour;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005993 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005994 QSort<Contour>(list.begin(), list.end() - 1);
5995}
5996
caryclark@google.comf839c032012-10-26 21:03:50 +00005997static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005998 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005999}
6000
caryclark@google.com10227bf2012-12-28 22:10:41 +00006001static bool lessThan(SkTDArray<double>& distances, const int one, const int two) {
6002 return distances[one] < distances[two];
6003}
caryclark@google.comf839c032012-10-26 21:03:50 +00006004 /*
6005 check start and end of each contour
6006 if not the same, record them
6007 match them up
6008 connect closest
6009 reassemble contour pieces into new path
6010 */
6011static void assemble(const PathWrapper& path, PathWrapper& simple) {
6012#if DEBUG_PATH_CONSTRUCTION
6013 SkDebugf("%s\n", __FUNCTION__);
6014#endif
6015 SkTArray<Contour> contours;
6016 EdgeBuilder builder(path, contours);
6017 builder.finish();
6018 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00006019 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00006020 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00006021 for (outer = 0; outer < count; ++outer) {
6022 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00006023 const SkPoint& eStart = eContour.start();
6024 const SkPoint& eEnd = eContour.end();
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006025#if DEBUG_ASSEMBLE
6026 SkDebugf("%s contour", __FUNCTION__);
6027 if (!approximatelyEqual(eStart, eEnd)) {
6028 SkDebugf("[%d]", runs.count());
6029 } else {
6030 SkDebugf(" ");
6031 }
skia.committer@gmail.com61b05dc2012-12-14 02:02:06 +00006032 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006033 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
6034#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00006035 if (approximatelyEqual(eStart, eEnd)) {
6036 eContour.toPath(simple);
6037 continue;
6038 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00006039 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00006040 }
6041 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00006042 if (count == 0) {
6043 return;
6044 }
caryclark@google.comf839c032012-10-26 21:03:50 +00006045 SkTDArray<int> sLink, eLink;
6046 sLink.setCount(count);
6047 eLink.setCount(count);
caryclark@google.com10227bf2012-12-28 22:10:41 +00006048 int rIndex, iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00006049 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00006050 sLink[rIndex] = eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006051 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00006052 SkTDArray<double> distances;
6053 const int ends = count * 2; // all starts and ends
6054 const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
6055 distances.setCount(entries);
6056 for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
6057 outer = runs[rIndex >> 1];
caryclark@google.com0b7da432012-10-31 19:00:20 +00006058 const Contour& oContour = contours[outer];
caryclark@google.com10227bf2012-12-28 22:10:41 +00006059 const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
6060 const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
6061 * ends - rIndex - 1;
6062 for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
6063 int inner = runs[iIndex >> 1];
caryclark@google.com0b7da432012-10-31 19:00:20 +00006064 const Contour& iContour = contours[inner];
caryclark@google.com10227bf2012-12-28 22:10:41 +00006065 const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
6066 double dx = iPt.fX - oPt.fX;
6067 double dy = iPt.fY - oPt.fY;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006068 double dist = dx * dx + dy * dy;
caryclark@google.com10227bf2012-12-28 22:10:41 +00006069 distances[row + iIndex] = dist; // oStart distance from iStart
6070 }
caryclark@google.comf839c032012-10-26 21:03:50 +00006071 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00006072 SkTDArray<int> sortedDist;
6073 sortedDist.setCount(entries);
6074 for (rIndex = 0; rIndex < entries; ++rIndex) {
6075 sortedDist[rIndex] = rIndex;
6076 }
6077 QSort<SkTDArray<double>, int>(distances, sortedDist.begin(), sortedDist.end() - 1, lessThan);
6078 int remaining = count; // number of start/end pairs
6079 for (rIndex = 0; rIndex < entries; ++rIndex) {
6080 int pair = sortedDist[rIndex];
6081 int row = pair / ends;
6082 int col = pair - row * ends;
6083 int thingOne = row < col ? row : ends - row - 2;
6084 int ndxOne = thingOne >> 1;
6085 bool endOne = thingOne & 1;
6086 int* linkOne = endOne ? eLink.begin() : sLink.begin();
6087 if (linkOne[ndxOne] != INT_MAX) {
6088 continue;
6089 }
6090 int thingTwo = row < col ? col : ends - row + col - 1;
6091 int ndxTwo = thingTwo >> 1;
6092 bool endTwo = thingTwo & 1;
6093 int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
6094 if (linkTwo[ndxTwo] != INT_MAX) {
6095 continue;
6096 }
6097 SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
6098 bool flip = endOne == endTwo;
6099 linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
6100 linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
6101 if (!--remaining) {
6102 break;
6103 }
6104 }
6105 SkASSERT(!remaining);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006106#if DEBUG_ASSEMBLE
6107 for (rIndex = 0; rIndex < count; ++rIndex) {
6108 int s = sLink[rIndex];
6109 int e = eLink[rIndex];
6110 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
6111 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
caryclark@google.comf839c032012-10-26 21:03:50 +00006112 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006113#endif
6114 rIndex = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00006115 do {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006116 bool forward = true;
6117 bool first = true;
6118 int sIndex = sLink[rIndex];
6119 SkASSERT(sIndex != INT_MAX);
6120 sLink[rIndex] = INT_MAX;
6121 int eIndex;
6122 if (sIndex < 0) {
6123 eIndex = sLink[~sIndex];
6124 sLink[~sIndex] = INT_MAX;
6125 } else {
6126 eIndex = eLink[sIndex];
6127 eLink[sIndex] = INT_MAX;
6128 }
6129 SkASSERT(eIndex != INT_MAX);
6130#if DEBUG_ASSEMBLE
6131 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
skia.committer@gmail.com61b05dc2012-12-14 02:02:06 +00006132 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
6133 eIndex < 0 ? ~eIndex : eIndex);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006134#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00006135 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006136 outer = runs[rIndex];
6137 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00006138 if (first) {
caryclark@google.comf839c032012-10-26 21:03:50 +00006139 first = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006140 const SkPoint* startPtr = &contour.start();
caryclark@google.comf839c032012-10-26 21:03:50 +00006141 simple.deferredMove(startPtr[0]);
6142 }
caryclark@google.comf839c032012-10-26 21:03:50 +00006143 if (forward) {
6144 contour.toPartialForward(simple);
caryclark@google.comf839c032012-10-26 21:03:50 +00006145 } else {
6146 contour.toPartialBackward(simple);
caryclark@google.comf839c032012-10-26 21:03:50 +00006147 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006148#if DEBUG_ASSEMBLE
6149 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
skia.committer@gmail.com61b05dc2012-12-14 02:02:06 +00006150 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006151 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
6152#endif
6153 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
caryclark@google.comf839c032012-10-26 21:03:50 +00006154 simple.close();
caryclark@google.comf839c032012-10-26 21:03:50 +00006155 break;
6156 }
caryclark@google.comf839c032012-10-26 21:03:50 +00006157 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006158 eIndex = eLink[rIndex];
6159 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00006160 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006161 if (eIndex >= 0) {
6162 SkASSERT(sLink[eIndex] == rIndex);
6163 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00006164 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006165 SkASSERT(eLink[~eIndex] == ~rIndex);
6166 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00006167 }
6168 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006169 eIndex = sLink[rIndex];
6170 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00006171 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006172 if (eIndex >= 0) {
6173 SkASSERT(eLink[eIndex] == rIndex);
6174 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00006175 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006176 SkASSERT(sLink[~eIndex] == ~rIndex);
6177 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00006178 }
6179 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00006180 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00006181 if (rIndex < 0) {
6182 forward ^= 1;
6183 rIndex = ~rIndex;
6184 }
6185 } while (true);
6186 for (rIndex = 0; rIndex < count; ++rIndex) {
6187 if (sLink[rIndex] != INT_MAX) {
6188 break;
6189 }
6190 }
6191 } while (rIndex < count);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006192#if DEBUG_ASSEMBLE
6193 for (rIndex = 0; rIndex < count; ++rIndex) {
6194 SkASSERT(sLink[rIndex] == INT_MAX);
6195 SkASSERT(eLink[rIndex] == INT_MAX);
6196 }
6197#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00006198}
6199
6200void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006201 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00006202 result.reset();
6203 result.setFillType(SkPath::kEvenOdd_FillType);
6204 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006205
6206 // turn path into list of segments
6207 SkTArray<Contour> contours;
6208 // FIXME: add self-intersecting cubics' T values to segment
6209 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00006210 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006211 SkTDArray<Contour*> contourList;
caryclark@google.com4eeda372012-12-06 21:47:48 +00006212 makeContourList(contours, contourList, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006213 Contour** currentPtr = contourList.begin();
6214 if (!currentPtr) {
6215 return;
6216 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00006217 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006218 // find all intersections between segments
6219 do {
6220 Contour** nextPtr = currentPtr;
6221 Contour* current = *currentPtr++;
6222 Contour* next;
6223 do {
6224 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00006225 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00006226 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00006227 // eat through coincident edges
caryclark@google.com4eeda372012-12-06 21:47:48 +00006228 coincidenceCheck(contourList, 0);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00006229 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00006230 sortSegments(contourList);
caryclark@google.com0b7da432012-10-31 19:00:20 +00006231#if DEBUG_ACTIVE_SPANS
6232 debugShowActiveSpans(contourList);
6233#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006234 // construct closed contours
caryclark@google.com3586ece2012-12-27 18:46:58 +00006235 if (builder.xorMask() == kWinding_Mask ? bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00006236 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006237 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00006238 SkPath temp;
6239 temp.setFillType(SkPath::kEvenOdd_FillType);
6240 PathWrapper assembled(temp);
6241 assemble(simple, assembled);
6242 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00006243 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006244}