blob: 31e59ccda1fb4d2c7858139b732b215a12b7b93b [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
caryclark@google.com47580692012-07-23 12:14:49 +000020// An Edge is a Segment generated from a Span
caryclark@google.com15fa1382012-05-07 20:49:36 +000021
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022// FIXME: remove once debugging is complete
caryclark@google.com47580692012-07-23 12:14:49 +000023#ifdef SK_DEBUG
24int gDebugMaxWindSum = SK_MaxS32;
25int gDebugMaxWindValue = SK_MaxS32;
26#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +000027
caryclark@google.comf839c032012-10-26 21:03:50 +000028#define PIN_ADD_T 0
caryclark@google.com0b7da432012-10-31 19:00:20 +000029#define TRY_ROTATE 1
caryclark@google.coma461ff02012-10-11 12:54:23 +000030
caryclark@google.com47580692012-07-23 12:14:49 +000031#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.com6ec15262012-11-16 20:16:50 +000032#define FORCE_RELEASE 0 // set force release to 1 for multiple thread -- no debugging
caryclark@google.comfa0588f2012-04-26 21:01:06 +000033
caryclark@google.com31143cf2012-11-09 22:14:19 +000034#if FORCE_RELEASE || defined SK_RELEASE
caryclark@google.com47580692012-07-23 12:14:49 +000035
36const bool gRunTestsInOneThread = false;
37
38#define DEBUG_ACTIVE_SPANS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000039#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000040#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000041#define DEBUG_ANGLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000042#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000043#define DEBUG_CROSS 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000044#define DEBUG_MARK_DONE 0
caryclark@google.comf839c032012-10-26 21:03:50 +000045#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com729e1c42012-11-21 21:36:34 +000046#define DEBUG_SHOW_WINDING 0
caryclark@google.com47580692012-07-23 12:14:49 +000047#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000048#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000049#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000050
51#else
52
caryclark@google.com47580692012-07-23 12:14:49 +000053const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000054
caryclark@google.comc91dfe42012-10-16 12:06:27 +000055#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000056#define DEBUG_ADD_INTERSECTING_TS 1
57#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000058#define DEBUG_ANGLE 1
59#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000060#define DEBUG_CROSS 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000061#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000062#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com729e1c42012-11-21 21:36:34 +000063#define DEBUG_SHOW_WINDING 0
caryclark@google.com47580692012-07-23 12:14:49 +000064#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000065#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000066#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000067
68#endif
69
caryclark@google.com6aea33f2012-10-09 14:11:58 +000070#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000071
caryclark@google.comfa0588f2012-04-26 21:01:06 +000072#if DEBUG_DUMP
73static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000074// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000075static int gContourID;
76static int gSegmentID;
77#endif
78
caryclark@google.com8dcf1142012-07-02 20:27:02 +000079#ifndef DEBUG_TEST
80#define DEBUG_TEST 0
81#endif
82
caryclark@google.com32546db2012-08-31 20:55:07 +000083#define MAKE_CONST_LINE(line, pts) \
84 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
85#define MAKE_CONST_QUAD(quad, pts) \
86 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
87 {pts[2].fX, pts[2].fY}}
88#define MAKE_CONST_CUBIC(cubic, pts) \
89 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
90 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
91
caryclark@google.comfa0588f2012-04-26 21:01:06 +000092static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
93 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000094 MAKE_CONST_LINE(aLine, a);
95 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +000096 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
97}
98
99static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
100 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000101 MAKE_CONST_QUAD(aQuad, a);
102 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000103 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000104}
105
caryclark@google.com32546db2012-08-31 20:55:07 +0000106static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000107 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000108 MAKE_CONST_CUBIC(aCubic, a);
109 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000110 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
111}
112
113static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
114 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000115 MAKE_CONST_QUAD(aQuad, a);
116 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000117#define TRY_QUARTIC_SOLUTION 1
118#if TRY_QUARTIC_SOLUTION
119 intersect2(aQuad, bQuad, intersections);
120#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000121 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000122#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000123 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000124}
125
126static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
127 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000128 MAKE_CONST_CUBIC(aCubic, a);
129 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000130 intersect(aCubic, bCubic, intersections);
131 return intersections.fUsed;
132}
133
134static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
135 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000136 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000137 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
138}
139
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000140static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
141 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000142 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000143 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
144}
145
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000146static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
147 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000148 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000149 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
150}
151
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000152static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
153 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000154 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000155 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
156}
157
158static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
159 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000160 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000161 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
162}
163
164static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
165 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000166 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000167 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000168}
169
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000170static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
171 SkScalar , SkScalar , bool , Intersections& ) = {
172 NULL,
173 VLineIntersect,
174 VQuadIntersect,
175 VCubicIntersect
176};
177
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000178static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000179 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000180 double x, y;
181 xy_at_t(line, t, x, y);
182 out->fX = SkDoubleToScalar(x);
183 out->fY = SkDoubleToScalar(y);
184}
185
186static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000187 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000188 double x, y;
189 xy_at_t(quad, t, x, y);
190 out->fX = SkDoubleToScalar(x);
191 out->fY = SkDoubleToScalar(y);
192}
193
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000194static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
195 MAKE_CONST_QUAD(quad, a);
196 xy_at_t(quad, t, out->x, out->y);
197}
198
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000199static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000200 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000201 double x, y;
202 xy_at_t(cubic, t, x, y);
203 out->fX = SkDoubleToScalar(x);
204 out->fY = SkDoubleToScalar(y);
205}
206
207static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
208 NULL,
209 LineXYAtT,
210 QuadXYAtT,
211 CubicXYAtT
212};
213
214static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000215 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000216 double x;
217 xy_at_t(aLine, t, x, *(double*) 0);
218 return SkDoubleToScalar(x);
219}
220
221static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000222 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000223 double x;
224 xy_at_t(quad, t, x, *(double*) 0);
225 return SkDoubleToScalar(x);
226}
227
228static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000229 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000230 double x;
231 xy_at_t(cubic, t, x, *(double*) 0);
232 return SkDoubleToScalar(x);
233}
234
235static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
236 NULL,
237 LineXAtT,
238 QuadXAtT,
239 CubicXAtT
240};
241
242static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000243 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000244 double y;
245 xy_at_t(aLine, t, *(double*) 0, y);
246 return SkDoubleToScalar(y);
247}
248
249static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000250 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000251 double y;
252 xy_at_t(quad, t, *(double*) 0, y);
253 return SkDoubleToScalar(y);
254}
255
256static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000257 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000258 double y;
259 xy_at_t(cubic, t, *(double*) 0, y);
260 return SkDoubleToScalar(y);
261}
262
263static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
264 NULL,
265 LineYAtT,
266 QuadYAtT,
267 CubicYAtT
268};
269
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000270static SkScalar LineDXAtT(const SkPoint a[2], double ) {
271 return a[1].fX - a[0].fX;
272}
273
274static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000275 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000276 double x;
277 dxdy_at_t(quad, t, x, *(double*) 0);
278 return SkDoubleToScalar(x);
279}
280
281static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000282 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000283 double x;
284 dxdy_at_t(cubic, t, x, *(double*) 0);
285 return SkDoubleToScalar(x);
286}
287
288static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
289 NULL,
290 LineDXAtT,
291 QuadDXAtT,
292 CubicDXAtT
293};
294
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000295static void LineSubDivide(const SkPoint a[2], double startT, double endT,
296 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000297 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000298 _Line dst;
299 sub_divide(aLine, startT, endT, dst);
300 sub[0].fX = SkDoubleToScalar(dst[0].x);
301 sub[0].fY = SkDoubleToScalar(dst[0].y);
302 sub[1].fX = SkDoubleToScalar(dst[1].x);
303 sub[1].fY = SkDoubleToScalar(dst[1].y);
304}
305
306static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
307 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000308 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000309 Quadratic dst;
310 sub_divide(aQuad, startT, endT, dst);
311 sub[0].fX = SkDoubleToScalar(dst[0].x);
312 sub[0].fY = SkDoubleToScalar(dst[0].y);
313 sub[1].fX = SkDoubleToScalar(dst[1].x);
314 sub[1].fY = SkDoubleToScalar(dst[1].y);
315 sub[2].fX = SkDoubleToScalar(dst[2].x);
316 sub[2].fY = SkDoubleToScalar(dst[2].y);
317}
318
319static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
320 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000321 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000322 Cubic dst;
323 sub_divide(aCubic, startT, endT, dst);
324 sub[0].fX = SkDoubleToScalar(dst[0].x);
325 sub[0].fY = SkDoubleToScalar(dst[0].y);
326 sub[1].fX = SkDoubleToScalar(dst[1].x);
327 sub[1].fY = SkDoubleToScalar(dst[1].y);
328 sub[2].fX = SkDoubleToScalar(dst[2].x);
329 sub[2].fY = SkDoubleToScalar(dst[2].y);
330 sub[3].fX = SkDoubleToScalar(dst[3].x);
331 sub[3].fY = SkDoubleToScalar(dst[3].y);
332}
333
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000334static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
335 SkPoint []) = {
336 NULL,
337 LineSubDivide,
338 QuadSubDivide,
339 CubicSubDivide
340};
341
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000342static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000343 _Line sub) {
344 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000345 _Line dst;
346 sub_divide(aLine, startT, endT, dst);
347 sub[0] = dst[0];
348 sub[1] = dst[1];
349}
350
351static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000352 Quadratic sub) {
353 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000354 Quadratic dst;
355 sub_divide(aQuad, startT, endT, dst);
356 sub[0] = dst[0];
357 sub[1] = dst[1];
358 sub[2] = dst[2];
359}
360
361static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000362 Cubic sub) {
363 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000364 Cubic dst;
365 sub_divide(aCubic, startT, endT, dst);
366 sub[0] = dst[0];
367 sub[1] = dst[1];
368 sub[2] = dst[2];
369 sub[3] = dst[3];
370}
371
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000372#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000373static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
374 SkRect& bounds) {
375 SkPoint dst[3];
376 QuadSubDivide(a, startT, endT, dst);
377 bounds.fLeft = bounds.fRight = dst[0].fX;
378 bounds.fTop = bounds.fBottom = dst[0].fY;
379 for (int index = 1; index < 3; ++index) {
380 bounds.growToInclude(dst[index].fX, dst[index].fY);
381 }
382}
383
384static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
385 SkRect& bounds) {
386 SkPoint dst[4];
387 CubicSubDivide(a, startT, endT, dst);
388 bounds.fLeft = bounds.fRight = dst[0].fX;
389 bounds.fTop = bounds.fBottom = dst[0].fY;
390 for (int index = 1; index < 4; ++index) {
391 bounds.growToInclude(dst[index].fX, dst[index].fY);
392 }
393}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000394#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000395
caryclark@google.com15fa1382012-05-07 20:49:36 +0000396static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000397 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000398 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000399 Quadratic dst;
400 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000401 if (order == 2) { // quad became line
402 for (int index = 0; index < order; ++index) {
403 SkPoint* pt = reducePts.append();
404 pt->fX = SkDoubleToScalar(dst[index].x);
405 pt->fY = SkDoubleToScalar(dst[index].y);
406 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000407 }
408 return (SkPath::Verb) (order - 1);
409}
410
411static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
412 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000413 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000414 Cubic dst;
415 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000416 if (order == 2 || order == 3) { // cubic became line or quad
417 for (int index = 0; index < order; ++index) {
418 SkPoint* pt = reducePts.append();
419 pt->fX = SkDoubleToScalar(dst[index].x);
420 pt->fY = SkDoubleToScalar(dst[index].y);
421 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000422 }
423 return (SkPath::Verb) (order - 1);
424}
425
caryclark@google.com15fa1382012-05-07 20:49:36 +0000426static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000427 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000428 return isLinear(aQuad, 0, 2);
429}
430
431static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000432 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000433 return isLinear(aCubic, 0, 3);
434}
435
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000436static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000437 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000438 double x[2];
439 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000440 xy_at_t(aLine, endT, x[1], *(double*) 0);
441 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000442}
443
444static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000445 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000446 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000447}
448
449static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000450 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000451 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000452}
453
454static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
455 NULL,
456 LineLeftMost,
457 QuadLeftMost,
458 CubicLeftMost
459};
460
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000461#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000462static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
463 Intersections& intersections) {
464 MAKE_CONST_QUAD(aQuad, a);
465 MAKE_CONST_LINE(bLine, b);
466 return intersectRay(aQuad, bLine, intersections);
467}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000468#endif
469
470static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
471 Intersections& intersections) {
472 MAKE_CONST_QUAD(aQuad, a);
473 return intersectRay(aQuad, bLine, intersections);
474}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000475
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000476class Segment;
477
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000478struct Span {
479 Segment* fOther;
480 mutable SkPoint fPt; // lazily computed as needed
481 double fT;
482 double fOtherT; // value at fOther[fOtherIndex].fT
483 int fOtherIndex; // can't be used during intersection
caryclark@google.com31143cf2012-11-09 22:14:19 +0000484 int fWindSum; // accumulated from contours surrounding this one.
485 int fOppSum; // for binary operators: the opposite winding sum
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000486 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000487 int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000488 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000489 bool fUnsortableStart; // set when start is part of an unsortable pair
490 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.comf839c032012-10-26 21:03:50 +0000491 bool fTiny; // if set, span may still be considered once for edge following
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000492};
493
caryclark@google.com15fa1382012-05-07 20:49:36 +0000494// sorting angles
495// given angles of {dx dy ddx ddy dddx dddy} sort them
496class Angle {
497public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000498 // FIXME: this is bogus for quads and cubics
499 // if the quads and cubics' line from end pt to ctrl pt are coincident,
500 // there's no obvious way to determine the curve ordering from the
501 // derivatives alone. In particular, if one quadratic's coincident tangent
502 // is longer than the other curve, the final control point can place the
503 // longer curve on either side of the shorter one.
504 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
505 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000506
caryclark@google.com32546db2012-08-31 20:55:07 +0000507 /*(
508 for quads and cubics, set up a parameterized line (e.g. LineParameters )
509 for points [0] to [1]. See if point [2] is on that line, or on one side
510 or the other. If it both quads' end points are on the same side, choose
511 the shorter tangent. If the tangents are equal, choose the better second
512 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000513
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000514 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000515 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000516 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000517 double y = dy();
518 double ry = rh.dy();
519 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
520 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000521 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000522 double x = dx();
523 double rx = rh.dx();
524 if (y == 0 && ry == 0 && x * rx < 0) {
525 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000526 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000527 double x_ry = x * ry;
528 double rx_y = rx * y;
529 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000530 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000531 return cmp < 0;
532 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000533 if (approximately_zero(x_ry) && approximately_zero(rx_y)
534 && !approximately_zero_squared(cmp)) {
535 return cmp < 0;
536 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000537 // at this point, the initial tangent line is coincident
caryclark@google.com31143cf2012-11-09 22:14:19 +0000538 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
539 || !approximately_zero(rh.fSide))) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000540 // FIXME: running demo will trigger this assertion
541 // (don't know if commenting out will trigger further assertion or not)
542 // commenting it out allows demo to run in release, though
543 // SkASSERT(fSide != rh.fSide);
544 return fSide < rh.fSide;
545 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000546 // see if either curve can be lengthened and try the tangent compare again
547 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
548 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
549 Angle longer = *this;
550 Angle rhLonger = rh;
551 if (longer.lengthen() | rhLonger.lengthen()) {
552 return longer < rhLonger;
553 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000554 // what if we extend in the other direction?
555 longer = *this;
556 rhLonger = rh;
557 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
558 return longer < rhLonger;
559 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000560 }
caryclark@google.com185c7c42012-10-19 18:26:24 +0000561 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
caryclark@google.com31143cf2012-11-09 22:14:19 +0000562 || (rh.fVerb == SkPath::kLine_Verb
563 && approximately_zero(rx) && approximately_zero(ry))) {
caryclark@google.com185c7c42012-10-19 18:26:24 +0000564 // See general unsortable comment below. This case can happen when
565 // one line has a non-zero change in t but no change in x and y.
566 fUnsortable = true;
567 rh.fUnsortable = true;
568 return this < &rh; // even with no solution, return a stable sort
569 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000570 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
571 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000572 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000573 // end of the shorter tangent to midway between the end points
574 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000575 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
576 double len = fTangent1.normalSquared();
577 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000578 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000579 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000580 int roots, rroots;
581 bool flip = false;
582 do {
583 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
584 double midX = (q[0].x + q[2].x) / 2;
585 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000586 ray[0] = q[1];
587 ray[1].x = midX;
588 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000589 SkASSERT(ray[0] != ray[1]);
590 roots = QuadRayIntersect(fPts, ray, i);
591 rroots = QuadRayIntersect(rh.fPts, ray, ri);
592 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000593 if (roots == 0 || rroots == 0) {
594 // FIXME: we don't have a solution in this case. The interim solution
595 // is to mark the edges as unsortable, exclude them from this and
596 // future computations, and allow the returned path to be fragmented
597 fUnsortable = true;
598 rh.fUnsortable = true;
599 return this < &rh; // even with no solution, return a stable sort
600 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000601 _Point loc;
602 double best = SK_ScalarInfinity;
603 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000604 int index;
605 for (index = 0; index < roots; ++index) {
606 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000607 dx = loc.x - ray[0].x;
608 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000609 dist = dx * dx + dy * dy;
610 if (best > dist) {
611 best = dist;
612 }
613 }
614 for (index = 0; index < rroots; ++index) {
615 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000616 dx = loc.x - ray[0].x;
617 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000618 dist = dx * dx + dy * dy;
619 if (best > dist) {
620 return fSide < 0;
621 }
622 }
623 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000624 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000625
caryclark@google.com47580692012-07-23 12:14:49 +0000626 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000627 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000628 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000629
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000630 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000631 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000632 }
633
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000634 int end() const {
635 return fEnd;
636 }
637
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000638 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000639 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000640 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000641
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000642 bool lengthen() {
643 int newEnd = fEnd;
644 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
645 fEnd = newEnd;
646 setSpans();
647 return true;
648 }
649 return false;
650 }
651
caryclark@google.coma461ff02012-10-11 12:54:23 +0000652 bool reverseLengthen() {
653 if (fReversed) {
654 return false;
655 }
656 int newEnd = fStart;
657 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
658 fEnd = newEnd;
659 fReversed = true;
660 setSpans();
661 return true;
662 }
663 return false;
664 }
665
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000666 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000667 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000668 fSegment = segment;
669 fStart = start;
670 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000671 fPts = orig;
672 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000673 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000674 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000675 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000676 setSpans();
677 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000678
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000679 void setSpans() {
680 double startT = (*fSpans)[fStart].fT;
681 double endT = (*fSpans)[fEnd].fT;
682 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000683 case SkPath::kLine_Verb:
684 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000685 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000686 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
687 fTangent1.lineEndPoints(l);
caryclark@google.comf839c032012-10-26 21:03:50 +0000688 fUnsortable = dx() == 0 && dy() == 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000689 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000690 break;
691 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000692 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000693 fTangent1.quadEndPoints(fQ, 0, 1);
694 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000695 break;
696 case SkPath::kCubic_Verb:
697 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000698 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000699 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000700 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000701 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000702 default:
703 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000704 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000705 if (fUnsortable) {
706 return;
707 }
708 SkASSERT(fStart != fEnd);
709 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
710 for (int index = fStart; index != fEnd; index += step) {
711 if ((*fSpans)[index].fUnsortableStart) {
712 fUnsortable = true;
713 return;
714 }
715 if (index != fStart && (*fSpans)[index].fUnsortableEnd) {
716 fUnsortable = true;
717 return;
718 }
719 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000720 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000721
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000722 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000723 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000724 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000725
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000726 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000727 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000728 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000729
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000730 const SkTDArray<Span>* spans() const {
731 return fSpans;
732 }
733
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000734 int start() const {
735 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000736 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000737
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000738 bool unsortable() const {
739 return fUnsortable;
740 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000741
caryclark@google.comc899ad92012-08-23 15:24:42 +0000742#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000743 const SkPoint* pts() const {
744 return fPts;
745 }
746
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000747 SkPath::Verb verb() const {
748 return fVerb;
749 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000750
caryclark@google.comc899ad92012-08-23 15:24:42 +0000751 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000752 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000753 }
754#endif
755
caryclark@google.com15fa1382012-05-07 20:49:36 +0000756private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000757 const SkPoint* fPts;
758 Quadratic fQ;
759 SkPath::Verb fVerb;
760 double fSide;
761 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000762 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000763 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000764 int fStart;
765 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000766 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000767 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000768};
769
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000770// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000771struct Bounds : public SkRect {
772 static bool Intersects(const Bounds& a, const Bounds& b) {
773 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
774 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
775 }
776
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000777 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
778 if (left < fLeft) {
779 fLeft = left;
780 }
781 if (top < fTop) {
782 fTop = top;
783 }
784 if (right > fRight) {
785 fRight = right;
786 }
787 if (bottom > fBottom) {
788 fBottom = bottom;
789 }
790 }
791
792 void add(const Bounds& toAdd) {
793 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
794 }
795
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000796 bool isEmpty() {
797 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000798 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000799 || isnan(fLeft) || isnan(fRight)
800 || isnan(fTop) || isnan(fBottom);
801 }
802
803 void setCubicBounds(const SkPoint a[4]) {
804 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000805 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000806 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000807 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
808 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000809 }
810
811 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000812 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000813 _Rect dRect;
814 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000815 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
816 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000817 }
818};
819
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000820// OPTIMIZATION: does the following also work, and is it any faster?
821// return outerWinding * innerWinding > 0
822// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
caryclark@google.com2ddff932012-08-07 21:25:27 +0000823static bool useInnerWinding(int outerWinding, int innerWinding) {
824 SkASSERT(outerWinding != innerWinding);
825 int absOut = abs(outerWinding);
826 int absIn = abs(innerWinding);
827 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
828 if (outerWinding * innerWinding < 0) {
829#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000830 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000831 outerWinding, innerWinding, result ? "true" : "false");
832#endif
833 }
834 return result;
835}
836
caryclark@google.com31143cf2012-11-09 22:14:19 +0000837static const bool gOpLookup[][2][2] = {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000838 // ==0 !=0
839 // b a b a
840 {{true , false}, {false, true }}, // a - b
841 {{false, false}, {true , true }}, // a & b
842 {{true , true }, {false, false}}, // a | b
843 {{true , true }, {true , true }}, // a ^ b
844};
845
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000846static bool activeOp(bool angleIsOp, int otherNonZero, int otherCoin, ShapeOp op) {
847 if (otherNonZero != otherCoin) {
848 return op == kIntersect_Op || op == kUnion_Op;
849 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000850 return gOpLookup[op][otherNonZero][angleIsOp];
caryclark@google.com235f56a2012-09-14 14:19:30 +0000851}
852
caryclark@google.comf839c032012-10-26 21:03:50 +0000853// wrap path to keep track of whether the contour is initialized and non-empty
854class PathWrapper {
855public:
856 PathWrapper(SkPath& path)
857 : fPathPtr(&path)
858 {
859 init();
860 }
861
862 void close() {
863 if (!fHasMove) {
864 return;
865 }
866 bool callClose = isClosed();
867 lineTo();
868 if (fEmpty) {
869 return;
870 }
871 if (callClose) {
872 #if DEBUG_PATH_CONSTRUCTION
873 SkDebugf("path.close();\n");
874 #endif
875 fPathPtr->close();
876 }
877 init();
878 }
879
880 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
881 lineTo();
882 moveTo();
883#if DEBUG_PATH_CONSTRUCTION
884 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
885 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
886#endif
887 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
888 fDefer[0] = fDefer[1] = pt3;
889 fEmpty = false;
890 }
891
892 void deferredLine(const SkPoint& pt) {
893 if (pt == fDefer[1]) {
894 return;
895 }
896 if (changedSlopes(pt)) {
897 lineTo();
898 fDefer[0] = fDefer[1];
899 }
900 fDefer[1] = pt;
901 }
902
903 void deferredMove(const SkPoint& pt) {
904 fMoved = true;
905 fHasMove = true;
906 fEmpty = true;
907 fDefer[0] = fDefer[1] = pt;
908 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000909
caryclark@google.comf839c032012-10-26 21:03:50 +0000910 void deferredMoveLine(const SkPoint& pt) {
911 if (!fHasMove) {
912 deferredMove(pt);
913 }
914 deferredLine(pt);
915 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000916
caryclark@google.comf839c032012-10-26 21:03:50 +0000917 bool hasMove() const {
918 return fHasMove;
919 }
920
921 void init() {
922 fEmpty = true;
923 fHasMove = false;
924 fMoved = false;
925 }
926
927 bool isClosed() const {
928 return !fEmpty && fFirstPt == fDefer[1];
929 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000930
caryclark@google.comf839c032012-10-26 21:03:50 +0000931 void lineTo() {
932 if (fDefer[0] == fDefer[1]) {
933 return;
934 }
935 moveTo();
936 fEmpty = false;
937#if DEBUG_PATH_CONSTRUCTION
938 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
939#endif
940 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
941 fDefer[0] = fDefer[1];
942 }
943
944 const SkPath* nativePath() const {
945 return fPathPtr;
946 }
947
948 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
949 lineTo();
950 moveTo();
951#if DEBUG_PATH_CONSTRUCTION
952 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
953 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
954#endif
955 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
956 fDefer[0] = fDefer[1] = pt2;
957 fEmpty = false;
958 }
959
960protected:
961 bool changedSlopes(const SkPoint& pt) const {
962 if (fDefer[0] == fDefer[1]) {
963 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000964 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000965 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
966 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
967 SkScalar lineDx = pt.fX - fDefer[1].fX;
968 SkScalar lineDy = pt.fY - fDefer[1].fY;
969 return deferDx * lineDy != deferDy * lineDx;
970 }
971
972 void moveTo() {
973 if (!fMoved) {
974 return;
975 }
976 fFirstPt = fDefer[0];
977#if DEBUG_PATH_CONSTRUCTION
978 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
979#endif
980 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
981 fMoved = false;
982 }
983
984private:
985 SkPath* fPathPtr;
986 SkPoint fDefer[2];
987 SkPoint fFirstPt;
988 bool fEmpty;
989 bool fHasMove;
990 bool fMoved;
991};
992
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000993class Segment {
994public:
995 Segment() {
996#if DEBUG_DUMP
997 fID = ++gSegmentID;
998#endif
999 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001000
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001001 bool operator<(const Segment& rh) const {
1002 return fBounds.fTop < rh.fBounds.fTop;
1003 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001004
caryclark@google.com9764cc62012-07-12 19:29:45 +00001005 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
1006 if (activeAngleInner(index, done, angles)) {
1007 return true;
1008 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001009 double referenceT = fTs[index].fT;
1010 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001011 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001012 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001013 return true;
1014 }
1015 }
1016 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001017 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001018 return true;
1019 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001020 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001021 return false;
1022 }
1023
caryclark@google.com9764cc62012-07-12 19:29:45 +00001024 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001025 Span* span = &fTs[index];
1026 Segment* other = span->fOther;
1027 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001028 return other->activeAngleInner(oIndex, done, angles);
1029 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001030
caryclark@google.com9764cc62012-07-12 19:29:45 +00001031 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001032 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001033 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001034 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001035 if (upSpan.fWindValue) {
1036 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001037 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001038 done++;
1039 } else if (upSpan.fWindSum != SK_MinS32) {
1040 return true;
1041 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001042 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001043 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001044 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001045 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001046 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001047 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001048 if (downSpan.fWindValue) {
1049 addAngle(angles, index, prev);
1050 if (downSpan.fDone) {
1051 done++;
1052 } else if (downSpan.fWindSum != SK_MinS32) {
1053 return true;
1054 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001055 }
1056 }
1057 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001058 }
1059
caryclark@google.comf839c032012-10-26 21:03:50 +00001060 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001061 SkASSERT(!done());
1062 int count = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001063 result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001064 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001065 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001066 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001067 const Span& span = fTs[index];
1068 if (span.fUnsortableStart | lastUnsortable) {
1069 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001070 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001071 if (!span.fDone | !lastDone) {
1072 const SkPoint& xy = xyAtT(index);
1073 if (result.fY < xy.fY) {
1074 goto next;
1075 }
1076 if (result.fY == xy.fY && result.fX < xy.fX) {
1077 goto next;
1078 }
1079 result = xy;
1080 }
1081 next:
1082 lastDone = span.fDone;
1083 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001084 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001085 SkASSERT(result.fY < SK_ScalarMax);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001086 }
1087
1088 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001089 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001090 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001091#if DEBUG_ANGLE
caryclark@google.com31143cf2012-11-09 22:14:19 +00001092 if (angles.count() > 1 && !fTs[start].fTiny) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001093 SkPoint angle0Pt, newPt;
1094 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1095 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1096 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1097 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1098 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1099 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001100#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001101 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001102 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001103
caryclark@google.com2ddff932012-08-07 21:25:27 +00001104 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001105 double oEnd) {
1106 int tIndex = -1;
1107 int tCount = fTs.count();
1108 int oIndex = -1;
1109 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001110 do {
1111 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001112 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001113 int tIndexStart = tIndex;
1114 do {
1115 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001116 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001117 int oIndexStart = oIndex;
1118 double nextT;
1119 do {
1120 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001121 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001122 double oNextT;
1123 do {
1124 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001125 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001126 // at this point, spans before and after are at:
1127 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1128 // if tIndexStart == 0, no prior span
1129 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001130
caryclark@google.comcc905052012-07-25 20:59:42 +00001131 // advance the span with zero winding
1132 // if the following span exists (not past the end, non-zero winding)
1133 // connect the two edges
1134 if (!fTs[tIndexStart].fWindValue) {
1135 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1136 #if DEBUG_CONCIDENT
1137 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1138 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001139 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1140 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001141 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001142 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001143 }
1144 if (nextT < 1 && fTs[tIndex].fWindValue) {
1145 #if DEBUG_CONCIDENT
1146 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1147 __FUNCTION__, fID, other.fID, tIndex,
1148 fTs[tIndex].fT, xyAtT(tIndex).fX,
1149 xyAtT(tIndex).fY);
1150 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001151 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001152 }
1153 } else {
1154 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1155 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1156 #if DEBUG_CONCIDENT
1157 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1158 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001159 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1160 other.xyAtT(oIndexStart).fY);
1161 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001162 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001163 }
1164 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1165 #if DEBUG_CONCIDENT
1166 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1167 __FUNCTION__, fID, other.fID, oIndex,
1168 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1169 other.xyAtT(oIndex).fY);
1170 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1171 #endif
1172 }
1173 }
1174 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001175
caryclark@google.comcc905052012-07-25 20:59:42 +00001176 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1177 double oEnd) {
1178 // walk this to outsideTs[0]
1179 // walk other to outsideTs[1]
1180 // if either is > 0, add a pointer to the other, copying adjacent winding
1181 int tIndex = -1;
1182 int oIndex = -1;
1183 double tStart = outsideTs[0];
1184 double oStart = outsideTs[1];
1185 do {
1186 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001187 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001188 do {
1189 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001190 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001191 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001192 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001193 }
1194 tStart = fTs[tIndex].fT;
1195 oStart = other.fTs[oIndex].fT;
1196 do {
1197 double nextT;
1198 do {
1199 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001200 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001201 tStart = nextT;
1202 do {
1203 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001204 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001205 oStart = nextT;
1206 if (tStart == 1 && oStart == 1) {
1207 break;
1208 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001209 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001210 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001211 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001212
caryclark@google.com235f56a2012-09-14 14:19:30 +00001213 void addCubic(const SkPoint pts[4], bool operand) {
1214 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001215 fBounds.setCubicBounds(pts);
1216 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001217
caryclark@google.comf839c032012-10-26 21:03:50 +00001218 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001219 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001220 const SkPoint* ePtr;
1221 int lastT = fTs.count() - 1;
1222 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1223 ePtr = fPts;
1224 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001225 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001226 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1227 ePtr = edge;
1228 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001229 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001230 bool reverse = ePtr == fPts && start != 0;
1231 if (reverse) {
1232 path.deferredMoveLine(ePtr[fVerb]);
1233 switch (fVerb) {
1234 case SkPath::kLine_Verb:
1235 path.deferredLine(ePtr[0]);
1236 break;
1237 case SkPath::kQuad_Verb:
1238 path.quadTo(ePtr[1], ePtr[0]);
1239 break;
1240 case SkPath::kCubic_Verb:
1241 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1242 break;
1243 default:
1244 SkASSERT(0);
1245 }
1246 // return ePtr[0];
1247 } else {
1248 path.deferredMoveLine(ePtr[0]);
1249 switch (fVerb) {
1250 case SkPath::kLine_Verb:
1251 path.deferredLine(ePtr[1]);
1252 break;
1253 case SkPath::kQuad_Verb:
1254 path.quadTo(ePtr[1], ePtr[2]);
1255 break;
1256 case SkPath::kCubic_Verb:
1257 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1258 break;
1259 default:
1260 SkASSERT(0);
1261 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001262 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001263 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001264 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001265 }
1266
caryclark@google.com235f56a2012-09-14 14:19:30 +00001267 void addLine(const SkPoint pts[2], bool operand) {
1268 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001269 fBounds.set(pts, 2);
1270 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001271
caryclark@google.comf839c032012-10-26 21:03:50 +00001272#if 0
1273 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001274 const SkPoint& pt = xyAtT(tIndex);
1275 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001276 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001277 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001278 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001279 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001280#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001281
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001282 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001283 void addOtherT(int index, double otherT, int otherIndex) {
1284 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001285 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001286 if (precisely_less_than_zero(otherT)) {
1287 otherT = 0;
1288 } else if (precisely_greater_than_one(otherT)) {
1289 otherT = 1;
1290 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001291 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001292 span.fOtherT = otherT;
1293 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001294 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001295
caryclark@google.com235f56a2012-09-14 14:19:30 +00001296 void addQuad(const SkPoint pts[3], bool operand) {
1297 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001298 fBounds.setQuadBounds(pts);
1299 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001300
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001301 // Defer all coincident edge processing until
1302 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001303
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001304// no need to be tricky; insert in normal T order
1305// resolve overlapping ts when considering coincidence later
1306
1307 // add non-coincident intersection. Resulting edges are sorted in T.
1308 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001309 // FIXME: in the pathological case where there is a ton of intercepts,
1310 // binary search?
1311 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001312 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001313 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001314 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001315 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001316 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001317 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001318 newT = 1;
1319 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001320 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001321 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001322 // OPTIMIZATION: if there are three or more identical Ts, then
1323 // the fourth and following could be further insertion-sorted so
1324 // that all the edges are clockwise or counterclockwise.
1325 // This could later limit segment tests to the two adjacent
1326 // neighbors, although it doesn't help with determining which
1327 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001328 if (newT < fTs[index].fT) {
1329 insertedAt = index;
1330 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001331 }
1332 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001333 Span* span;
1334 if (insertedAt >= 0) {
1335 span = fTs.insert(insertedAt);
1336 } else {
1337 insertedAt = tCount;
1338 span = fTs.append();
1339 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001340 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001341 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001342 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001343 span->fWindSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001344 span->fOppSum = SK_MinS32;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001345 span->fWindValue = 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001346 span->fOppValue = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001347 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001348 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001349 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001350 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001351 span->fUnsortableStart = false;
1352 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001353 if (span - fTs.begin() > 0 && !span[-1].fDone
1354 && !precisely_negative(newT - span[-1].fT)
1355 // && approximately_negative(newT - span[-1].fT)
1356 && xyAtT(&span[-1]) == xyAtT(span)) {
1357 span[-1].fTiny = true;
1358 span[-1].fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001359 if (approximately_negative(newT - span[-1].fT)) {
1360 if (approximately_greater_than_one(newT)) {
1361 span[-1].fUnsortableStart = true;
1362 span[-2].fUnsortableEnd = true;
1363 }
1364 if (approximately_less_than_zero(span[-1].fT)) {
1365 span->fUnsortableStart = true;
1366 span[-1].fUnsortableEnd = true;
1367 }
1368 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001369 ++fDoneSpans;
1370 }
1371 if (fTs.end() - span > 1 && !span->fDone
1372 && !precisely_negative(span[1].fT - newT)
1373 // && approximately_negative(span[1].fT - newT)
1374 && xyAtT(&span[1]) == xyAtT(span)) {
1375 span->fTiny = true;
1376 span->fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001377 if (approximately_negative(span[1].fT - newT)) {
1378 if (approximately_greater_than_one(span[1].fT)) {
1379 span->fUnsortableStart = true;
1380 span[-1].fUnsortableEnd = true;
1381 }
1382 if (approximately_less_than_zero(newT)) {
1383 span[1].fUnsortableStart = true;
1384 span->fUnsortableEnd = true;
1385 }
1386 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001387 ++fDoneSpans;
1388 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001389 return insertedAt;
1390 }
1391
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001392 // set spans from start to end to decrement by one
1393 // note this walks other backwards
1394 // FIMXE: there's probably an edge case that can be constructed where
1395 // two span in one segment are separated by float epsilon on one span but
1396 // not the other, if one segment is very small. For this
1397 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001398 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001399 // sorted? It feels better in such a case to match the span's other span
1400 // pointer since both coincident segments must contain the same spans.
1401 void addTCancel(double startT, double endT, Segment& other,
1402 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001403 SkASSERT(!approximately_negative(endT - startT));
1404 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001405 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001406 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001407 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001408 ++index;
1409 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001410 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001411 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001412 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001413 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001414 Span* test = &fTs[index];
1415 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001416 SkTDArray<double> outsideTs;
1417 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001418 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001419 bool decrement = test->fWindValue && oTest->fWindValue && !binary;
caryclark@google.comcc905052012-07-25 20:59:42 +00001420 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001421 double testT = test->fT;
1422 double oTestT = oTest->fT;
1423 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001424 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001425 if (decrement) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001426 decrementSpan(span);
caryclark@google.com200c2112012-08-03 15:05:04 +00001427 } else if (track && span->fT < 1 && oTestT < 1) {
1428 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001429 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001430 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001431 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001432 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001433 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1434 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1435 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001436 while (approximately_negative(otherTMatchStart - oSpan->fT)
1437 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001438 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001439 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001440 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001441 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001442 other.decrementSpan(oSpan);
1443 } else if (track && oSpan->fT < 1 && testT < 1) {
1444 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001445 }
1446 if (!oIndex) {
1447 break;
1448 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001449 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001450 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001451 test = span;
1452 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001453 } while (!approximately_negative(endT - test->fT));
1454 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001455 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001456 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001457 double tStart = outsideTs[0];
1458 double oStart = outsideTs[1];
1459 addCancelOutsides(tStart, oStart, other, oEndT);
1460 int count = outsideTs.count();
1461 if (count > 2) {
1462 double tStart = outsideTs[count - 2];
1463 double oStart = outsideTs[count - 1];
1464 addCancelOutsides(tStart, oStart, other, oEndT);
1465 }
caryclark@google.com18063442012-07-25 12:05:18 +00001466 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001467 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001468 double tStart = oOutsideTs[0];
1469 double oStart = oOutsideTs[1];
1470 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001471 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001472 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00001473
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001474 int bumpCoincidentThis(const Span* oTest, const bool transfer, const bool decrementThis,
caryclark@google.com729e1c42012-11-21 21:36:34 +00001475 const bool thisXor, const int oXorMask, const bool opp, int index,
1476 SkTDArray<double>& outsideTs, SkTDArray<double>& xOutsideTs)
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001477 {
1478 Span* const test = &fTs[index];
1479 Span* end = test;
1480 const int startIndex = index;
1481 const double oStartT = oTest->fT;
1482 do {
1483 if (transfer) {
caryclark@google.com6ec15262012-11-16 20:16:50 +00001484 if (opp) {
1485 if (decrementThis) {
caryclark@google.com729e1c42012-11-21 21:36:34 +00001486 zeroSpan(end, oStartT);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001487 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001488 } else {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001489 end->fWindValue += oTest->fOppValue;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001490 end->fOppValue = (end->fOppValue + oTest->fWindValue) & oXorMask;
1491 if (thisXor) {
1492 SkASSERT(end->fWindValue);
1493 if (!(end->fWindValue & 1)) {
1494 zeroSpan(end, oStartT);
1495 TrackOutside(outsideTs, end->fT, oStartT);
1496 }
1497 }
caryclark@google.com6ec15262012-11-16 20:16:50 +00001498 }
1499 } else if (!decrementThis & !thisXor) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001500 #ifdef SK_DEBUG
1501 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
1502 #endif
1503 ++(end->fWindValue);
1504 } else if (decrementSpan(end)) {
1505 TrackOutside(outsideTs, end->fT, oStartT);
1506 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001507 } else if (oTest->fWindValue) {
1508 SkASSERT(decrementThis);
1509 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1510 TrackOutside(xOutsideTs, end->fT, oStartT);
1511 }
1512 }
1513 end = &fTs[++index];
1514 } while (approximately_negative(end->fT - test->fT));
1515 return index;
1516 }
1517
1518 // because of the order in which coincidences are resolved, this and other
1519 // may not have the same intermediate points. Compute the corresponding
1520 // intermediate T values (using this as the master, other as the follower)
1521 // and walk other conditionally -- hoping that it catches up in the end
1522 int bumpCoincidentOther(const Span* test, const bool transfer, const bool decrementThis,
caryclark@google.com729e1c42012-11-21 21:36:34 +00001523 const bool otherXor, const int xorMask, const bool opp, const double tRatio,
1524 const double oEndT, int& oIndex, SkTDArray<double>& oOutsideTs)
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001525 {
1526 Span* const oTest = &fTs[oIndex];
1527 Span* oEnd = oTest;
1528 const double startT = test->fT;
1529 const int oStartIndex = oIndex;
1530 const double oStartT = oTest->fT;
1531 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
1532 while (!approximately_negative(oEndT - oEnd->fT)
1533 && approximately_negative(oEnd->fT - otherTMatch)) {
1534 if (transfer) {
caryclark@google.com6ec15262012-11-16 20:16:50 +00001535 if (opp) {
1536 if (decrementThis) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001537 oEnd->fWindValue += test->fOppValue;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001538 oEnd->fOppValue = (oEnd->fOppValue + test->fWindValue) & xorMask;
1539 if (otherXor) {
1540 SkASSERT(oEnd->fWindValue);
1541 if (!(oEnd->fWindValue & 1)) {
1542 zeroSpan(oEnd, startT);
1543 TrackOutside(oOutsideTs, oEnd->fT, startT);
1544 }
1545 }
caryclark@google.com6ec15262012-11-16 20:16:50 +00001546 } else {
caryclark@google.com729e1c42012-11-21 21:36:34 +00001547 zeroSpan(oEnd, startT);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001548 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001549 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00001550 } else if (decrementThis & !otherXor) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001551 #ifdef SK_DEBUG
1552 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1553 #endif
1554 ++(oEnd->fWindValue);
1555 } else if (decrementSpan(oEnd)) {
1556 TrackOutside(oOutsideTs, oEnd->fT, startT);
1557 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001558 } else if (test->fWindValue) {
1559 SkASSERT(decrementThis);
1560 if (oStartIndex > 0 && fTs[oStartIndex - 1].fWindValue) {
1561 SkASSERT(0); // track for later?
1562 }
1563 }
1564 oEnd = &fTs[++oIndex];
1565 }
1566 return oIndex;
1567 }
1568
1569 // FIXME: need to test this case:
1570 // contourA has two segments that are coincident
1571 // contourB has two segments that are coincident in the same place
1572 // each ends up with +2/0 pairs for winding count
1573 // since logic below doesn't transfer count (only increments/decrements) can this be
1574 // resolved to +4/0 ?
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001575
1576 // set spans from start to end to increment the greater by one and decrement
1577 // the lesser
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001578 void addTCoincident(bool thisXor, bool otherXor, double startT, double endT,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001579 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001580 SkASSERT(!approximately_negative(endT - startT));
1581 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001582 bool opp = fOperand ^ other.fOperand;
1583 if (!opp) {
1584 otherXor = thisXor;
1585 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001586 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001587 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001588 ++index;
1589 }
1590 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001591 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001592 ++oIndex;
1593 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001594 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001595 Span* test = &fTs[index];
1596 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001597 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001598 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001599 SkTDArray<double> oOutsideTs;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001600 int xorMask = thisXor ? 1 : -1;
1601 int oXorMask = otherXor ? 1 : -1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001602 do {
caryclark@google.com6ec15262012-11-16 20:16:50 +00001603 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001604 bool decrementThis = test->fWindValue < oTest->fWindValue ||
1605 (test->fWindValue == oTest->fWindValue && thisXor);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001606 if (decrementThis) {
caryclark@google.com729e1c42012-11-21 21:36:34 +00001607 oIndex = other.bumpCoincidentOther(test, transfer, decrementThis, otherXor, xorMask,
1608 opp, tRatio, oEndT, oIndex, oOutsideTs);
1609 index = bumpCoincidentThis(oTest, transfer, decrementThis, thisXor, oXorMask, opp,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001610 index, outsideTs, xOutsideTs);
1611 } else {
caryclark@google.com729e1c42012-11-21 21:36:34 +00001612 index = bumpCoincidentThis(oTest, transfer, decrementThis, thisXor, oXorMask, opp,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001613 index, outsideTs, xOutsideTs);
caryclark@google.com729e1c42012-11-21 21:36:34 +00001614 oIndex = other.bumpCoincidentOther(test, transfer, decrementThis, otherXor, xorMask,
1615 opp, tRatio, oEndT, oIndex, oOutsideTs);
caryclark@google.com59823f72012-08-09 18:17:47 +00001616 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001617 test = &fTs[index];
1618 oTest = &other.fTs[oIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001619 } while (!approximately_negative(endT - test->fT));
1620 SkASSERT(approximately_negative(oTest->fT - oEndT));
1621 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001622 if (!done()) {
1623 if (outsideTs.count()) {
1624 addCoinOutsides(outsideTs, other, oEndT);
1625 }
1626 if (xOutsideTs.count()) {
1627 addCoinOutsides(xOutsideTs, other, oEndT);
1628 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001629 }
1630 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001631 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001632 }
1633 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001634
caryclark@google.comcc905052012-07-25 20:59:42 +00001635 // FIXME: this doesn't prevent the same span from being added twice
1636 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001637 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001638 int tCount = fTs.count();
1639 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1640 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001641 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001642 break;
1643 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001644 if (approximately_negative(span.fT - t) && span.fOther == &other
1645 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001646#if DEBUG_ADD_T_PAIR
1647 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1648 __FUNCTION__, fID, t, other.fID, otherT);
1649#endif
1650 return;
1651 }
1652 }
caryclark@google.com47580692012-07-23 12:14:49 +00001653#if DEBUG_ADD_T_PAIR
1654 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1655 __FUNCTION__, fID, t, other.fID, otherT);
1656#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001657 int insertedAt = addT(t, &other);
1658 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001659 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001660 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001661 matchWindingValue(insertedAt, t, borrowWind);
1662 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001663 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001664
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001665 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001666 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001667 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1668 addAngle(angles, end, start);
1669 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001670 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001671 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001672 int tIndex = nextExactSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001673 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001674 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001675 }
1676 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001677
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001678 const Bounds& bounds() const {
1679 return fBounds;
1680 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001681
caryclark@google.com31143cf2012-11-09 22:14:19 +00001682 void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001683 double referenceT = fTs[index].fT;
1684 int lesser = index;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001685 while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand)
1686 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00001687 buildAnglesInner(lesser, angles);
1688 }
1689 do {
1690 buildAnglesInner(index, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001691 } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand)
1692 && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001693 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001694
1695 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1696 Span* span = &fTs[index];
1697 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001698 // if there is only one live crossing, and no coincidence, continue
1699 // in the same direction
1700 // if there is coincidence, the only choice may be to reverse direction
1701 // find edge on either side of intersection
1702 int oIndex = span->fOtherIndex;
1703 // if done == -1, prior span has already been processed
1704 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001705 int next = other->nextExactSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001706 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001707 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001708 next = other->nextExactSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001709 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001710 // add candidate into and away from junction
1711 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001712 }
1713
caryclark@google.com6ec15262012-11-16 20:16:50 +00001714 int computeSum(int startIndex, int endIndex, int* oppoSum) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001715 SkTDArray<Angle> angles;
1716 addTwoAngles(startIndex, endIndex, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001717 buildAngles(endIndex, angles, false);
caryclark@google.comd1688742012-09-18 20:08:37 +00001718 // OPTIMIZATION: check all angles to see if any have computed wind sum
1719 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001720 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001721 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001722#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001723 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001724#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001725 if (!sortable) {
1726 return SK_MinS32;
1727 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001728 int angleCount = angles.count();
1729 const Angle* angle;
1730 const Segment* base;
1731 int winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001732 int oWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001733 int firstIndex = 0;
1734 do {
1735 angle = sorted[firstIndex];
1736 base = angle->segment();
1737 winding = base->windSum(angle);
1738 if (winding != SK_MinS32) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001739 oWinding = base->oppSum(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001740 break;
1741 }
1742 if (++firstIndex == angleCount) {
1743 return SK_MinS32;
1744 }
1745 } while (true);
1746 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001747 int spanWinding = base->spanSign(angle);
1748 bool inner = useInnerWinding(winding + spanWinding, winding);
1749 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001750 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001751 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001752 inner ? winding + spanWinding : winding);
1753 #endif
1754 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001755 winding += spanWinding;
1756 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00001757 if (oppoSum) {
1758 int oppoWinding = base->oppSign(angle);
1759 if (useInnerWinding(oWinding + oppoWinding, oWinding)) {
1760 oWinding += oppoWinding;
1761 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001762 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001763 #if DEBUG_SORT
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001764 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001765 #endif
1766 int nextIndex = firstIndex + 1;
1767 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001768 winding -= base->spanSign(angle);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001769 oWinding -= base->oppSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001770 do {
1771 if (nextIndex == angleCount) {
1772 nextIndex = 0;
1773 }
1774 angle = sorted[nextIndex];
1775 Segment* segment = angle->segment();
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001776 bool opp = base->fOperand ^ segment->fOperand;
1777 int maxWinding, oMaxWinding;
1778 int spanSign = segment->spanSign(angle);
1779 int oppoSign = segment->oppSign(angle);
1780 if (opp) {
1781 oMaxWinding = oWinding;
1782 oWinding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001783 maxWinding = winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001784 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001785 winding -= oppoSign;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001786 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001787 } else {
1788 maxWinding = winding;
1789 winding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001790 oMaxWinding = oWinding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001791 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001792 oWinding -= oppoSign;
1793 }
1794 }
1795 if (segment->windSum(angle) == SK_MinS32) {
1796 if (opp) {
1797 if (useInnerWinding(oMaxWinding, oWinding)) {
1798 oMaxWinding = oWinding;
1799 }
1800 if (oppoSign && useInnerWinding(maxWinding, winding)) {
1801 maxWinding = winding;
1802 }
1803 segment->markAndChaseWinding(angle, oMaxWinding, maxWinding);
1804 } else {
1805 if (useInnerWinding(maxWinding, winding)) {
1806 maxWinding = winding;
1807 }
1808 if (oppoSign && useInnerWinding(oMaxWinding, oWinding)) {
1809 oMaxWinding = oWinding;
1810 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00001811 segment->markAndChaseWinding(angle, maxWinding, oppoSum ? oMaxWinding : 0);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001812 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001813 }
1814 } while (++nextIndex != lastIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001815 int minIndex = SkMin32(startIndex, endIndex);
1816 if (oppoSum) {
1817 *oppoSum = oppSum(minIndex);
1818 }
1819 return windSum(minIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001820 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001821
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001822 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001823 int bestT = -1;
1824 SkScalar top = bounds().fTop;
1825 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001826 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001827 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001828 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001829 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001830 if (fTs[start].fWindValue == 0) {
1831 continue;
1832 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001833 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001834 double startT = fTs[start].fT;
1835 double endT = fTs[end].fT;
1836 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001837 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001838 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001839 // FIXME: always use original and limit results to T values within
1840 // start t and end t.
1841 // OPTIMIZE: use specialty function that intersects ray with curve,
1842 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001843 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1844 false, intersections);
1845 if (pts == 0) {
1846 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001847 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001848 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1849 // if the intersection is edge on, wait for another one
1850 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001851 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001852 for (int index = 0; index < pts; ++index) {
1853 SkPoint pt;
1854 double foundT = intersections.fT[0][index];
1855 double testT = startT + (endT - startT) * foundT;
1856 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1857 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001858 if (fVerb > SkPath::kLine_Verb
1859 && !approximately_less_than_zero(foundT)
1860 && !approximately_greater_than_one(foundT)) {
1861 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1862 if (approximately_zero(dx)) {
1863 continue;
1864 }
1865 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001866 bestY = pt.fY;
1867 bestT = foundT < 1 ? start : end;
1868 hitT = testT;
1869 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001870 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001871 } while (fTs[end].fT != 1);
1872 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001873 }
caryclark@google.com18063442012-07-25 12:05:18 +00001874
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001875 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1876 // if a segment is connected to this one, consider it crossing
1877 int tIndex;
1878 if (fPts[0].fX == basePt.fX) {
1879 tIndex = 0;
1880 do {
1881 const Span& sSpan = fTs[tIndex];
1882 const Segment* sOther = sSpan.fOther;
1883 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1884 continue;
1885 }
1886 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1887 : sOther->fBounds.fRight > basePt.fX) {
1888 return true;
1889 }
1890 } while (fTs[++tIndex].fT == 0);
1891 }
1892 if (fPts[fVerb].fX == basePt.fX) {
1893 tIndex = fTs.count() - 1;
1894 do {
1895 const Span& eSpan = fTs[tIndex];
1896 const Segment* eOther = eSpan.fOther;
1897 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1898 continue;
1899 }
1900 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1901 : eOther->fBounds.fRight > basePt.fX) {
1902 return true;
1903 }
1904 } while (fTs[--tIndex].fT == 1);
1905 }
1906 return false;
1907 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001908
caryclark@google.com18063442012-07-25 12:05:18 +00001909 bool decrementSpan(Span* span) {
1910 SkASSERT(span->fWindValue > 0);
1911 if (--(span->fWindValue) == 0) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001912 if (!span->fDone) {
1913 span->fDone = true;
1914 ++fDoneSpans;
1915 }
caryclark@google.com18063442012-07-25 12:05:18 +00001916 return true;
1917 }
1918 return false;
1919 }
1920
caryclark@google.com15fa1382012-05-07 20:49:36 +00001921 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001922 SkASSERT(fDoneSpans <= fTs.count());
1923 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001924 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001925
caryclark@google.comf839c032012-10-26 21:03:50 +00001926 bool done(int min) const {
1927 return fTs[min].fDone;
1928 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001929
caryclark@google.com47580692012-07-23 12:14:49 +00001930 bool done(const Angle& angle) const {
caryclark@google.comf839c032012-10-26 21:03:50 +00001931 return done(SkMin32(angle.start(), angle.end()));
caryclark@google.com47580692012-07-23 12:14:49 +00001932 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001933
caryclark@google.com235f56a2012-09-14 14:19:30 +00001934 Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
caryclark@google.com31143cf2012-11-09 22:14:19 +00001935 int& nextStart, int& nextEnd, int& winding, int& oppWinding,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001936 int& spanWinding, int& oppSpanWinding, bool& unsortable, ShapeOp op,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001937 const int aXorMask, const int bXorMask) {
1938 const int startIndex = nextStart;
1939 const int endIndex = nextEnd;
1940 int outerWinding = winding;
1941 int innerWinding = winding + spanWinding;
1942 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00001943 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d oppWinding=%d\n",
1944 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001945 #endif
1946 if (useInnerWinding(outerWinding, innerWinding)) {
1947 outerWinding = innerWinding;
1948 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001949 int outerOppWinding = oppWinding;
1950 if (oppSpanWinding) {
1951 int innerOppWinding = oppWinding + oppSpanWinding;
1952 if (useInnerWinding(oppWinding, innerOppWinding)) {
1953 outerOppWinding = innerOppWinding;
1954 }
1955 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001956 SkASSERT(startIndex != endIndex);
1957 int count = fTs.count();
1958 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1959 : startIndex > 0);
1960 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001961 int end = nextExactSpan(startIndex, step);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001962 SkASSERT(end >= 0);
1963 Span* endSpan = &fTs[end];
1964 Segment* other;
1965 if (isSimple(end)) {
1966 // mark the smaller of startIndex, endIndex done, and all adjacent
1967 // spans with the same T value (but not 'other' spans)
1968 #if DEBUG_WINDING
1969 SkDebugf("%s simple\n", __FUNCTION__);
1970 #endif
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001971 markDone(SkMin32(startIndex, endIndex), outerWinding, outerOppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001972 other = endSpan->fOther;
1973 nextStart = endSpan->fOtherIndex;
1974 double startT = other->fTs[nextStart].fT;
1975 nextEnd = nextStart;
1976 do {
1977 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001978 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00001979 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001980 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1981 return other;
1982 }
1983 // more than one viable candidate -- measure angles to find best
1984 SkTDArray<Angle> angles;
1985 SkASSERT(startIndex - endIndex != 0);
1986 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1987 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001988 buildAngles(end, angles, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001989 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001990 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001991 int angleCount = angles.count();
1992 int firstIndex = findStartingEdge(sorted, startIndex, end);
1993 SkASSERT(firstIndex >= 0);
1994 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001995 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001996 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001997 if (!sortable) {
1998 unsortable = true;
1999 return NULL;
2000 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002001 SkASSERT(sorted[firstIndex]->segment() == this);
2002 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002003 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
2004 sorted[firstIndex]->sign());
caryclark@google.com235f56a2012-09-14 14:19:30 +00002005 #endif
2006 int aSumWinding = winding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002007 int bSumWinding = oppWinding;
2008 bool angleIsOp = sorted[firstIndex]->segment()->operand() ^ operand();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002009 const Angle* firstAngle = sorted[firstIndex];
2010 int angleSpan = spanSign(firstAngle);
2011 int oppoSign = oppSign(firstAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002012 if (angleIsOp) {
2013 bSumWinding -= angleSpan;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002014 aSumWinding -= oppoSign;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002015 } else {
2016 aSumWinding -= angleSpan;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002017 bSumWinding -= oppoSign;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002018 }
2019 int nextIndex = firstIndex + 1;
2020 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2021 const Angle* foundAngle = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002022 bool foundDone = false;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002023#define TWO_CHANNEL_DONE 0
2024#if TWO_CHANNEL_DONE
caryclark@google.com235f56a2012-09-14 14:19:30 +00002025 bool foundDone2 = false;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002026#define FOUND_DONE2 foundDone2
2027#else
2028#define FOUND_DONE2 foundDone
2029#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002030 // iterate through the angle, and compute everyone's winding
caryclark@google.com31143cf2012-11-09 22:14:19 +00002031 bool aAltFlipped = false;
2032 bool bAltFlipped = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002033 bool foundFlipped = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002034 int foundSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002035 int foundOppWinding = SK_MinS32;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002036 Segment* nextSegment;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002037 int aLastNonZeroSum = winding;
2038 int bLastNonZeroSum = oppWinding;
2039 bool foundOpp;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002040 do {
2041 if (nextIndex == angleCount) {
2042 nextIndex = 0;
2043 }
2044 const Angle* nextAngle = sorted[nextIndex];
2045 nextSegment = nextAngle->segment();
caryclark@google.comf839c032012-10-26 21:03:50 +00002046 bool nextDone = nextSegment->done(*nextAngle);
2047 bool nextTiny = nextSegment->tiny(*nextAngle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002048 angleIsOp = nextSegment->operand() ^ operand();
2049 int deltaSum = nextSegment->spanSign(nextAngle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002050 int oppDeltaSum = nextSegment->oppSign(nextAngle);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002051 int maxWinding, xorMask, sumWinding, oMaxWinding, oSumWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002052 bool otherNonZero, altFlipped, otherCoin;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002053 if (angleIsOp) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002054 maxWinding = bSumWinding;
2055 if (bSumWinding) {
2056 bLastNonZeroSum = bSumWinding;
2057 }
2058 bSumWinding -= deltaSum;
2059 sumWinding = bSumWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002060 otherNonZero = aSumWinding & aXorMask;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002061 xorMask = bXorMask;
2062 bAltFlipped ^= bLastNonZeroSum * bSumWinding < 0; // flip if different signs
2063 altFlipped = bAltFlipped;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002064 oMaxWinding = aSumWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002065 aSumWinding -= oppDeltaSum;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002066 oSumWinding = aSumWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002067 otherCoin = aSumWinding & aXorMask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002068 } else {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002069 maxWinding = aSumWinding;
2070 if (aSumWinding) {
2071 aLastNonZeroSum = aSumWinding;
2072 }
2073 aSumWinding -= deltaSum;
2074 sumWinding = aSumWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002075 otherNonZero = bSumWinding & bXorMask;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002076 xorMask = aXorMask;
2077 aAltFlipped ^= aLastNonZeroSum * aSumWinding < 0; // flip if different signs
2078 altFlipped = aAltFlipped;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002079 oMaxWinding = bSumWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002080 bSumWinding -= oppDeltaSum;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002081 oSumWinding = bSumWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002082 otherCoin = bSumWinding & bXorMask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002083 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002084 if (oppDeltaSum && useInnerWinding(oMaxWinding, oSumWinding)) {
2085 oMaxWinding = oSumWinding;
2086 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002087 bool opIsActive = activeOp(nextSegment->operand(), otherNonZero, otherCoin, op);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002088 if (!(sumWinding & xorMask)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002089 if (!active) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002090 markAndChaseDone(startIndex, endIndex, outerWinding, outerOppWinding);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002091 nextSegment->markAndChaseWinding(nextAngle, maxWinding, oMaxWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002092 #if DEBUG_WINDING
2093 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
2094 #endif
2095 return NULL;
2096 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002097 if (opIsActive && (!foundAngle || foundDone)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002098 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002099 foundDone = nextDone && !nextTiny;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002100 foundFlipped = altFlipped;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002101 foundSum = 0;
2102 foundOpp = angleIsOp;
caryclark@google.com729e1c42012-11-21 21:36:34 +00002103 foundOppWinding = oSumWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002104 }
2105 continue;
2106 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002107 if (opIsActive && !(maxWinding & xorMask) && (!foundAngle || FOUND_DONE2)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002108 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00002109 if (foundAngle && FOUND_DONE2) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002110 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
2111 }
2112 #endif
2113 foundAngle = nextAngle;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002114 FOUND_DONE2 = nextDone && !nextTiny;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002115 foundFlipped = altFlipped;
2116 foundSum = sumWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00002117 foundOpp = angleIsOp;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002118 foundOppWinding = oMaxWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002119 }
2120 if (nextSegment->done()) {
2121 continue;
2122 }
2123 // if the winding is non-zero, nextAngle does not connect to
2124 // current chain. If we haven't done so already, mark the angle
2125 // as done, record the winding value, and mark connected unambiguous
2126 // segments as well.
2127 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
2128 if (useInnerWinding(maxWinding, sumWinding)) {
2129 maxWinding = sumWinding;
2130 }
2131 Span* last;
2132 if (foundAngle) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002133 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding, oMaxWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002134 } else {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00002135 last = nextSegment->markAndChaseDone(nextAngle, maxWinding, oMaxWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002136 }
2137 if (last) {
2138 *chase.append() = last;
2139 }
2140 }
2141 } while (++nextIndex != lastIndex);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002142 markDone(SkMin32(startIndex, endIndex), outerWinding, outerOppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002143 if (!foundAngle) {
2144 return NULL;
2145 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002146 #if DEBUG_WINDING
2147 int oldSpanSign = spanSign(nextStart, nextEnd);
2148 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002149 nextStart = foundAngle->start();
2150 nextEnd = foundAngle->end();
2151 nextSegment = foundAngle->segment();
2152 int flipped = foundFlipped ? -1 : 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002153 int minStartEnd = SkMin32(nextStart, nextEnd);
2154 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(minStartEnd);
2155 oppSpanWinding = SkSign32(oppSpanWinding) * flipped * nextSegment->oppValue(minStartEnd);
2156
caryclark@google.com235f56a2012-09-14 14:19:30 +00002157 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00002158 SkDebugf("%s foundFlipped=%d spanWinding=%d oldSpanSign=%d spanSign=%d\n",
2159 __FUNCTION__, foundFlipped, spanWinding, oldSpanSign,
2160 nextSegment->spanSign(foundAngle));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002161 SkDebugf("%s foundOpp=%d oppWinding=%d oppSpanWinding=%d foundOppWinding=%d winding=%d"
2162 " foundSum=", __FUNCTION__, foundOpp, oppWinding, oppSpanWinding, foundOppWinding,
2163 winding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002164 if (foundSum == SK_MinS32) {
2165 SkDebugf("?");
2166 } else {
2167 SkDebugf("%d", foundSum);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002168 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002169 SkDebugf("\n");
2170 #endif
2171 if (oppWinding != foundOppWinding) {
2172 oppWinding = foundOppWinding;
2173 if (foundOpp) {
2174 SkASSERT(foundSum != SK_MinS32);
2175 winding = foundSum;
2176 spanWinding = nextSegment->spanSign(foundAngle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002177 oppSpanWinding = nextSegment->oppSign(foundAngle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002178 }
2179 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002180 return nextSegment;
2181 }
caryclark@google.com47580692012-07-23 12:14:49 +00002182
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002183 // so the span needs to contain the pairing info found here
2184 // this should include the winding computed for the edge, and
2185 // what edge it connects to, and whether it is discarded
2186 // (maybe discarded == abs(winding) > 1) ?
2187 // only need derivatives for duration of sorting, add a new struct
2188 // for pairings, remove extra spans that have zero length and
2189 // reference an unused other
2190 // for coincident, the last span on the other may be marked done
2191 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002192
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002193 // if loop is exhausted, contour may be closed.
2194 // FIXME: pass in close point so we can check for closure
2195
2196 // given a segment, and a sense of where 'inside' is, return the next
2197 // segment. If this segment has an intersection, or ends in multiple
2198 // segments, find the mate that continues the outside.
2199 // note that if there are multiples, but no coincidence, we can limit
2200 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002201
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002202 // mark found segments as done
2203
caryclark@google.com15fa1382012-05-07 20:49:36 +00002204 // start is the index of the beginning T of this edge
2205 // it is guaranteed to have an end which describes a non-zero length (?)
2206 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002207 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002208 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2209 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002210 const int startIndex = nextStart;
2211 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002212 int outerWinding = winding;
2213 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002214 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002215 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2216 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002217 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002218 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002219 outerWinding = innerWinding;
2220 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002221 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002222 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002223 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2224 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002225 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002226 int end = nextExactSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002227 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002228 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002229 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002230 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002231 // mark the smaller of startIndex, endIndex done, and all adjacent
2232 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002233 #if DEBUG_WINDING
2234 SkDebugf("%s simple\n", __FUNCTION__);
2235 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002236 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002237 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002238 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002239 double startT = other->fTs[nextStart].fT;
2240 nextEnd = nextStart;
2241 do {
2242 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002243 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002244 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00002245 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002246 return other;
2247 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002248 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002249 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002250 SkASSERT(startIndex - endIndex != 0);
2251 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002252 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002253 buildAngles(end, angles, false);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002254 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002255 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002256 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002257 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002258 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002259 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002260 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002261 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002262 if (!sortable) {
2263 unsortable = true;
2264 return NULL;
2265 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002266 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002267 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002268 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002269 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002270 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002271 int nextIndex = firstIndex + 1;
2272 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2273 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002274 // FIXME: found done logic probably fails if there are more than 4
2275 // sorted angles. It should bias towards the first and last undone
2276 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002277 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002278 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002279 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002280 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002281 bool altFlipped = false;
2282 bool foundFlipped = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002283 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002284 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002285 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002286 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002287 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002288 nextIndex = 0;
2289 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002290 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002291 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002292 if (sumWinding) {
2293 lastNonZeroSum = sumWinding;
2294 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002295 nextSegment = nextAngle->segment();
caryclark@google.comf839c032012-10-26 21:03:50 +00002296 bool nextDone = nextSegment->done(*nextAngle);
2297 bool nextTiny = nextSegment->tiny(*nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002298 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002299 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002300 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002301 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002302 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2303 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002304 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002305 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002306 if (!active) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002307 // FIXME: shouldn't this call mark and chase done ?
caryclark@google.com59823f72012-08-09 18:17:47 +00002308 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002309 // FIXME: shouldn't this call mark and chase winding ?
caryclark@google.com47580692012-07-23 12:14:49 +00002310 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002311 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002312 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002313 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002314 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002315 return NULL;
2316 }
caryclark@google.com47580692012-07-23 12:14:49 +00002317 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002318 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002319 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002320 foundFlipped = altFlipped;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002321 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002322 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002323 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002324
caryclark@google.com24bec792012-08-20 12:43:57 +00002325 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002326 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002327 if (foundAngle && foundDone2) {
2328 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002329 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002330 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002331 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002332 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002333 foundFlipped = altFlipped;
2334 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002335 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002336 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002337 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002338 }
2339 // if the winding is non-zero, nextAngle does not connect to
2340 // current chain. If we haven't done so already, mark the angle
2341 // as done, record the winding value, and mark connected unambiguous
2342 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002343 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002344 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002345 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002346 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002347 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002348 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002349 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002350 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002351 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002352 }
2353 if (last) {
2354 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002355 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002356 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002357 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002358 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002359 if (!foundAngle) {
2360 return NULL;
2361 }
2362 nextStart = foundAngle->start();
2363 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002364 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002365 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002366 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2367 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002368 if (winding) {
2369 #if DEBUG_WINDING
2370 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2371 if (foundSum == SK_MinS32) {
2372 SkDebugf("?");
2373 } else {
2374 SkDebugf("%d", foundSum);
2375 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002376 SkDebugf("\n");
2377 #endif
2378 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002379 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002380 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002381 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002382 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002383 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002384 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002385
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002386 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002387 const int startIndex = nextStart;
2388 const int endIndex = nextEnd;
2389 SkASSERT(startIndex != endIndex);
2390 int count = fTs.count();
2391 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2392 : startIndex > 0);
2393 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002394 int end = nextExactSpan(startIndex, step);
caryclark@google.com24bec792012-08-20 12:43:57 +00002395 SkASSERT(end >= 0);
2396 Span* endSpan = &fTs[end];
2397 Segment* other;
2398 markDone(SkMin32(startIndex, endIndex), 1);
2399 if (isSimple(end)) {
2400 #if DEBUG_WINDING
2401 SkDebugf("%s simple\n", __FUNCTION__);
2402 #endif
2403 other = endSpan->fOther;
2404 nextStart = endSpan->fOtherIndex;
2405 double startT = other->fTs[nextStart].fT;
2406 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002407 if ((approximately_less_than_zero(startT) && step < 0)
2408 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002409 step = -step;
2410 SkDEBUGCODE(firstLoop = false;)
2411 }
2412 do {
2413 nextEnd = nextStart;
2414 do {
2415 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002416 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002417 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002418 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2419 break;
2420 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002421 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002422 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002423 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002424 SkDEBUGCODE(firstLoop = false;)
2425 step = -step;
2426 } while (true);
2427 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2428 return other;
2429 }
2430 SkTDArray<Angle> angles;
2431 SkASSERT(startIndex - endIndex != 0);
2432 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2433 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002434 buildAngles(end, angles, false);
caryclark@google.com24bec792012-08-20 12:43:57 +00002435 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002436 bool sortable = SortAngles(angles, sorted);
caryclark@google.com24bec792012-08-20 12:43:57 +00002437 int angleCount = angles.count();
2438 int firstIndex = findStartingEdge(sorted, startIndex, end);
2439 SkASSERT(firstIndex >= 0);
2440 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002441 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002442 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002443 if (!sortable) {
2444 unsortable = true;
2445 return NULL;
2446 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002447 SkASSERT(sorted[firstIndex]->segment() == this);
2448 int nextIndex = firstIndex + 1;
2449 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2450 const Angle* nextAngle;
2451 Segment* nextSegment;
2452 do {
2453 if (nextIndex == angleCount) {
2454 nextIndex = 0;
2455 }
2456 nextAngle = sorted[nextIndex];
2457 nextSegment = nextAngle->segment();
2458 if (!nextSegment->done(*nextAngle)) {
2459 break;
2460 }
2461 if (++nextIndex == lastIndex) {
2462 return NULL;
2463 }
2464 } while (true);
2465 nextStart = nextAngle->start();
2466 nextEnd = nextAngle->end();
2467 return nextSegment;
2468 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002469
2470 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2471 int angleCount = sorted.count();
2472 int firstIndex = -1;
2473 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2474 const Angle* angle = sorted[angleIndex];
2475 if (angle->segment() == this && angle->start() == end &&
2476 angle->end() == start) {
2477 firstIndex = angleIndex;
2478 break;
2479 }
2480 }
2481 return firstIndex;
2482 }
2483
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002484 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002485 void findTooCloseToCall(bool thisXor, bool otherXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002486 int count = fTs.count();
2487 if (count < 3) { // require t=0, x, 1 at minimum
2488 return;
2489 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002490 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002491 int moCount;
2492 Span* match;
2493 Segment* mOther;
2494 do {
2495 match = &fTs[matchIndex];
2496 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002497 // FIXME: allow quads, cubics to be near coincident?
2498 if (mOther->fVerb == SkPath::kLine_Verb) {
2499 moCount = mOther->fTs.count();
2500 if (moCount >= 3) {
2501 break;
2502 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002503 }
2504 if (++matchIndex >= count) {
2505 return;
2506 }
2507 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002508 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002509 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002510 // look for a pair of nearby T values that map to the same (x,y) value
2511 // if found, see if the pair of other segments share a common point. If
2512 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002513 for (int index = matchIndex + 1; index < count; ++index) {
2514 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002515 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002516 continue;
2517 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002518 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002519 if (tOther->fVerb != SkPath::kLine_Verb) {
2520 continue; // FIXME: allow quads, cubics to be near coincident?
2521 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002522 int toCount = tOther->fTs.count();
2523 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002524 continue;
2525 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002526 const SkPoint* testPt = &xyAtT(test);
2527 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002528 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002529 moCount = toCount;
2530 match = test;
2531 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002532 matchPt = testPt;
2533 continue;
2534 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002535 int moStart = -1;
2536 int moEnd = -1;
2537 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002538 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002539 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002540 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002541 continue;
2542 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002543 if (moSpan.fOther == this) {
2544 if (moSpan.fOtherT == match->fT) {
2545 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002546 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002547 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002548 continue;
2549 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002550 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002551 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2552 moStart = -1;
2553 break;
2554 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002555 SkASSERT(moEnd == -1);
2556 moEnd = moIndex;
2557 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002558 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002559 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002560 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002561 continue;
2562 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002563 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002564 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002565 continue;
2566 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002567 int toStart = -1;
2568 int toEnd = -1;
2569 double toStartT, toEndT;
2570 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2571 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002572 if (toSpan.fDone) {
2573 continue;
2574 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002575 if (toSpan.fOther == this) {
2576 if (toSpan.fOtherT == test->fT) {
2577 toStart = toIndex;
2578 toStartT = toSpan.fT;
2579 }
2580 continue;
2581 }
2582 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002583 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2584 moStart = -1;
2585 break;
2586 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002587 SkASSERT(toEnd == -1);
2588 toEnd = toIndex;
2589 toEndT = toSpan.fT;
2590 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002591 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002592 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2593 if (toStart <= 0 || toEnd <= 0) {
2594 continue;
2595 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002596 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002597 continue;
2598 }
2599 // test to see if the segment between there and here is linear
2600 if (!mOther->isLinear(moStart, moEnd)
2601 || !tOther->isLinear(toStart, toEnd)) {
2602 continue;
2603 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002604 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002605 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002606 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002607 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002608 // FIXME: this is bogus for multiple ops
2609 // the xorMask needs to be accumulated from the union of the two
2610 // edges -- which means that the segment must have its own copy of the mask
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002611 mOther->addTCoincident(thisXor, otherXor,
2612 moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002613 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002614 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002615 }
2616
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002617 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002618 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002619 // a) mark spans with either end unsortable as done, or
2620 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2621 // when encountering an unsortable span
2622
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002623 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2624 // and use more concise logic like the old edge walker code?
2625 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002626 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002627 // iterate through T intersections and return topmost
2628 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002629 SkASSERT(!done());
2630 int firstT;
2631 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002632 SkPoint topPt;
2633 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002634 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002635 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002636 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002637 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002638 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002639 const Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00002640 if (span.fUnsortableStart | lastUnsortable) {
2641 goto next;
2642 }
2643 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002644 const SkPoint& intercept = xyAtT(&span);
2645 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2646 && topPt.fX > intercept.fX)) {
2647 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002648 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002649 } else if (topPt == intercept) {
2650 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002651 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002652 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002653 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002654 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002655 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002656 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002657 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002658 int step = 1;
2659 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002660 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002661 step = -1;
2662 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002663 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002664 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002665 // if the topmost T is not on end, or is three-way or more, find left
2666 // look for left-ness from tLeft to firstT (matching y of other)
2667 SkTDArray<Angle> angles;
2668 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002669 addTwoAngles(end, firstT, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002670 buildAngles(firstT, angles, true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002671 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002672 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002673 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002674 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002675 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00002676 if (!sortable) {
2677 return NULL;
2678 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002679 // skip edges that have already been processed
2680 firstT = -1;
2681 Segment* leftSegment;
2682 do {
2683 const Angle* angle = sorted[++firstT];
caryclark@google.comf839c032012-10-26 21:03:50 +00002684 SkASSERT(!angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002685 leftSegment = angle->segment();
2686 tIndex = angle->end();
2687 endIndex = angle->start();
2688 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002689 return leftSegment;
2690 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002691
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002692 // FIXME: not crazy about this
2693 // when the intersections are performed, the other index is into an
2694 // incomplete array. as the array grows, the indices become incorrect
2695 // while the following fixes the indices up again, it isn't smart about
2696 // skipping segments whose indices are already correct
2697 // assuming we leave the code that wrote the index in the first place
2698 void fixOtherTIndex() {
2699 int iCount = fTs.count();
2700 for (int i = 0; i < iCount; ++i) {
2701 Span& iSpan = fTs[i];
2702 double oT = iSpan.fOtherT;
2703 Segment* other = iSpan.fOther;
2704 int oCount = other->fTs.count();
2705 for (int o = 0; o < oCount; ++o) {
2706 Span& oSpan = other->fTs[o];
2707 if (oT == oSpan.fT && this == oSpan.fOther) {
2708 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002709 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002710 }
2711 }
2712 }
2713 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002714
caryclark@google.com495f8e42012-05-31 13:13:11 +00002715 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002716 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002717 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002718 SkASSERT(end >= 0);
2719 if (multipleSpans(end)) {
2720 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002721 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002722 const Span& endSpan = fTs[end];
2723 Segment* other = endSpan.fOther;
2724 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002725 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002726 Span* last = other->innerChaseDone(index, step, winding);
2727 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002728 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002729 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002730
caryclark@google.com31143cf2012-11-09 22:14:19 +00002731 Span* innerChaseDone(int index, int step, int winding, int oppWinding) {
2732 int end = nextExactSpan(index, step);
2733 SkASSERT(end >= 0);
2734 if (multipleSpans(end)) {
2735 return &fTs[end];
2736 }
2737 const Span& endSpan = fTs[end];
2738 Segment* other = endSpan.fOther;
2739 index = endSpan.fOtherIndex;
2740 int otherEnd = other->nextExactSpan(index, step);
2741 Span* last = other->innerChaseDone(index, step, winding, oppWinding);
2742 other->markDone(SkMin32(index, otherEnd), winding, oppWinding);
2743 return last;
2744 }
2745
2746
caryclark@google.com59823f72012-08-09 18:17:47 +00002747 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002748 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002749 SkASSERT(end >= 0);
2750 if (multipleSpans(end)) {
2751 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002752 }
2753 const Span& endSpan = fTs[end];
2754 Segment* other = endSpan.fOther;
2755 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002756 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002757 int min = SkMin32(index, otherEnd);
2758 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002759 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002760 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002761 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002762 Span* last = other->innerChaseWinding(index, step, winding);
2763 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002764 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002765 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002766
caryclark@google.com31143cf2012-11-09 22:14:19 +00002767 Span* innerChaseWinding(int index, int step, int winding, int oppWinding) {
2768 int end = nextExactSpan(index, step);
2769 SkASSERT(end >= 0);
2770 if (multipleSpans(end)) {
2771 return &fTs[end];
2772 }
2773 const Span& endSpan = fTs[end];
2774 Segment* other = endSpan.fOther;
2775 index = endSpan.fOtherIndex;
2776 int otherEnd = other->nextExactSpan(index, step);
2777 int min = SkMin32(index, otherEnd);
2778 if (other->fTs[min].fWindSum != SK_MinS32) {
2779 SkASSERT(other->fTs[min].fWindSum == winding);
2780 return NULL;
2781 }
2782 Span* last = other->innerChaseWinding(index, step, winding, oppWinding);
2783 other->markWinding(min, winding, oppWinding);
2784 return last;
2785 }
2786
caryclark@google.com235f56a2012-09-14 14:19:30 +00002787 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2788 fDoneSpans = 0;
2789 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002790 fPts = pts;
2791 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002792 }
2793
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002794 bool intersected() const {
2795 return fTs.count() > 0;
2796 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002797
2798 bool isConnected(int startIndex, int endIndex) const {
2799 return fTs[startIndex].fWindSum != SK_MinS32
2800 || fTs[endIndex].fWindSum != SK_MinS32;
2801 }
2802
caryclark@google.com235f56a2012-09-14 14:19:30 +00002803 bool isHorizontal() const {
2804 return fBounds.fTop == fBounds.fBottom;
2805 }
2806
caryclark@google.com15fa1382012-05-07 20:49:36 +00002807 bool isLinear(int start, int end) const {
2808 if (fVerb == SkPath::kLine_Verb) {
2809 return true;
2810 }
2811 if (fVerb == SkPath::kQuad_Verb) {
2812 SkPoint qPart[3];
2813 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2814 return QuadIsLinear(qPart);
2815 } else {
2816 SkASSERT(fVerb == SkPath::kCubic_Verb);
2817 SkPoint cPart[4];
2818 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2819 return CubicIsLinear(cPart);
2820 }
2821 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002822
2823 // OPTIMIZE: successive calls could start were the last leaves off
2824 // or calls could specialize to walk forwards or backwards
2825 bool isMissing(double startT) const {
2826 size_t tCount = fTs.count();
2827 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002828 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002829 return false;
2830 }
2831 }
2832 return true;
2833 }
2834
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002835 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002836 int count = fTs.count();
2837 if (count == 2) {
2838 return true;
2839 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002840 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002841 if (approximately_less_than_zero(t)) {
2842 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002843 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002844 if (approximately_greater_than_one(t)) {
2845 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002846 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002847 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002848 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002849
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002850 bool isVertical() const {
2851 return fBounds.fLeft == fBounds.fRight;
2852 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002853
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002854 SkScalar leftMost(int start, int end) const {
2855 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2856 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002857
caryclark@google.com495f8e42012-05-31 13:13:11 +00002858 // this span is excluded by the winding rule -- chase the ends
2859 // as long as they are unambiguous to mark connections as done
2860 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002861 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002862 int index = angle->start();
2863 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002864 return markAndChaseDone(index, endIndex, winding);
2865 }
2866
2867 Span* markAndChaseDone(const Angle* angle, int winding, int oppWinding) {
2868 int index = angle->start();
2869 int endIndex = angle->end();
2870 return markAndChaseDone(index, endIndex, winding, oppWinding);
2871 }
2872
2873 Span* markAndChaseDone(int index, int endIndex, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002874 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002875 Span* last = innerChaseDone(index, step, winding);
2876 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002877 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002878 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002879
caryclark@google.com31143cf2012-11-09 22:14:19 +00002880 Span* markAndChaseDone(int index, int endIndex, int winding, int oppWinding) {
2881 int step = SkSign32(endIndex - index);
2882 Span* last = innerChaseDone(index, step, winding, oppWinding);
2883 markDone(SkMin32(index, endIndex), winding, oppWinding);
2884 return last;
2885 }
2886
caryclark@google.com59823f72012-08-09 18:17:47 +00002887 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002888 int index = angle->start();
2889 int endIndex = angle->end();
2890 int min = SkMin32(index, endIndex);
2891 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002892 Span* last = innerChaseWinding(index, step, winding);
2893 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002894 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002895 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002896
caryclark@google.com31143cf2012-11-09 22:14:19 +00002897 Span* markAndChaseWinding(const Angle* angle, int winding, int oppWinding) {
2898 int index = angle->start();
2899 int endIndex = angle->end();
2900 int min = SkMin32(index, endIndex);
2901 int step = SkSign32(endIndex - index);
2902 Span* last = innerChaseWinding(index, step, winding, oppWinding);
2903 markWinding(min, winding, oppWinding);
2904 return last;
2905 }
2906
caryclark@google.com495f8e42012-05-31 13:13:11 +00002907 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002908 // This may be called when the segment is already marked done. While this
2909 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002910 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002911 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002912 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002913 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002914 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002915 double referenceT = fTs[index].fT;
2916 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002917 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2918 markOneDone(__FUNCTION__, lesser, winding);
2919 }
2920 do {
2921 markOneDone(__FUNCTION__, index, winding);
2922 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002923 }
2924
2925 void markDone(int index, int winding, int oppWinding) {
2926 // SkASSERT(!done());
2927 SkASSERT(winding);
2928 double referenceT = fTs[index].fT;
2929 int lesser = index;
2930 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2931 markOneDone(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002932 }
2933 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002934 markOneDone(__FUNCTION__, index, winding, oppWinding);
2935 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002936 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002937
caryclark@google.com24bec792012-08-20 12:43:57 +00002938 void markOneDone(const char* funName, int tIndex, int winding) {
2939 Span* span = markOneWinding(funName, tIndex, winding);
2940 if (!span) {
2941 return;
2942 }
2943 span->fDone = true;
2944 fDoneSpans++;
2945 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002946
caryclark@google.com31143cf2012-11-09 22:14:19 +00002947 void markOneDone(const char* funName, int tIndex, int winding, int oppWinding) {
2948 Span* span = markOneWinding(funName, tIndex, winding, oppWinding);
2949 if (!span) {
2950 return;
2951 }
2952 span->fDone = true;
2953 fDoneSpans++;
2954 }
2955
caryclark@google.com24bec792012-08-20 12:43:57 +00002956 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2957 Span& span = fTs[tIndex];
2958 if (span.fDone) {
2959 return NULL;
2960 }
2961 #if DEBUG_MARK_DONE
2962 debugShowNewWinding(funName, span, winding);
2963 #endif
2964 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002965 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002966 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002967 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002968 span.fWindSum = winding;
2969 return &span;
2970 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002971
caryclark@google.com31143cf2012-11-09 22:14:19 +00002972 Span* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding) {
2973 Span& span = fTs[tIndex];
2974 if (span.fDone) {
2975 return NULL;
2976 }
2977 #if DEBUG_MARK_DONE
2978 debugShowNewWinding(funName, span, winding, oppWinding);
2979 #endif
2980 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
2981 #ifdef SK_DEBUG
2982 SkASSERT(abs(winding) <= gDebugMaxWindSum);
2983 #endif
2984 span.fWindSum = winding;
2985 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
2986 #ifdef SK_DEBUG
2987 SkASSERT(abs(oppWinding) <= gDebugMaxWindSum);
2988 #endif
2989 span.fOppSum = oppWinding;
2990 return &span;
2991 }
2992
caryclark@google.comf839c032012-10-26 21:03:50 +00002993 // note that just because a span has one end that is unsortable, that's
2994 // not enough to mark it done. The other end may be sortable, allowing the
2995 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002996 void markUnsortable(int start, int end) {
2997 Span* span = &fTs[start];
2998 if (start < end) {
2999 span->fUnsortableStart = true;
3000 } else {
3001 --span;
3002 span->fUnsortableEnd = true;
3003 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003004 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003005 return;
3006 }
3007 span->fDone = true;
3008 fDoneSpans++;
3009 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003010
caryclark@google.com59823f72012-08-09 18:17:47 +00003011 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003012 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003013 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003014 double referenceT = fTs[index].fT;
3015 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00003016 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3017 markOneWinding(__FUNCTION__, lesser, winding);
3018 }
3019 do {
3020 markOneWinding(__FUNCTION__, index, winding);
3021 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00003022 }
3023
3024 void markWinding(int index, int winding, int oppWinding) {
3025 // SkASSERT(!done());
3026 SkASSERT(winding);
3027 double referenceT = fTs[index].fT;
3028 int lesser = index;
3029 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3030 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003031 }
3032 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003033 markOneWinding(__FUNCTION__, index, winding, oppWinding);
3034 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003035 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003036
caryclark@google.com2ddff932012-08-07 21:25:27 +00003037 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003038 int nextDoorWind = SK_MaxS32;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003039 int nextOppWind = SK_MaxS32;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003040 if (tIndex > 0) {
3041 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003042 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003043 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003044 nextOppWind = below.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003045 }
3046 }
3047 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3048 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003049 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003050 nextDoorWind = above.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003051 nextOppWind = above.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003052 }
3053 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003054 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
3055 const Span& below = fTs[tIndex - 1];
3056 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003057 nextOppWind = below.fOppValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003058 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003059 if (nextDoorWind != SK_MaxS32) {
3060 Span& newSpan = fTs[tIndex];
3061 newSpan.fWindValue = nextDoorWind;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003062 newSpan.fOppValue = nextOppWind;
caryclark@google.comf839c032012-10-26 21:03:50 +00003063 if (!nextDoorWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003064 newSpan.fDone = true;
3065 ++fDoneSpans;
3066 }
3067 }
3068 }
3069
caryclark@google.com9764cc62012-07-12 19:29:45 +00003070 // return span if when chasing, two or more radiating spans are not done
3071 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
3072 // candidate and the remaining spans have windValue == 0 (canceled by
3073 // coincidence). The coincident edges could either be removed altogether,
3074 // or this code could be more complicated in detecting this case. Worth it?
3075 bool multipleSpans(int end) const {
3076 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003077 }
3078
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003079 // This has callers for two different situations: one establishes the end
3080 // of the current span, and one establishes the beginning of the next span
3081 // (thus the name). When this is looking for the end of the current span,
3082 // coincidence is found when the beginning Ts contain -step and the end
3083 // contains step. When it is looking for the beginning of the next, the
3084 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003085 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003086 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003087 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00003088 int count = fTs.count();
3089 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00003090 while (step > 0 ? ++to < count : --to >= 0) {
3091 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003092 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003093 continue;
3094 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003095 return to;
3096 }
3097 return -1;
3098 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00003099
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003100 // FIXME
3101 // this returns at any difference in T, vs. a preset minimum. It may be
3102 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003103 // OPTIMIZATION splitting this into separate loops for up/down steps
3104 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003105 int nextExactSpan(int from, int step) const {
3106 const Span& fromSpan = fTs[from];
3107 int count = fTs.count();
3108 int to = from;
3109 while (step > 0 ? ++to < count : --to >= 0) {
3110 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00003111 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003112 continue;
3113 }
3114 return to;
3115 }
3116 return -1;
3117 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003118
caryclark@google.com235f56a2012-09-14 14:19:30 +00003119 bool operand() const {
3120 return fOperand;
3121 }
3122
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003123 int oppSign(const Angle* angle) const {
3124 SkASSERT(angle->segment() == this);
3125 return oppSign(angle->start(), angle->end());
3126 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003127
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003128 int oppSign(int startIndex, int endIndex) const {
3129 int result = startIndex < endIndex ? -fTs[startIndex].fOppValue
3130 : fTs[endIndex].fOppValue;
3131#if DEBUG_WIND_BUMP
3132 SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
3133#endif
3134 return result;
3135 }
3136
caryclark@google.com31143cf2012-11-09 22:14:19 +00003137 int oppSum(int tIndex) const {
3138 return fTs[tIndex].fOppSum;
3139 }
3140
3141 int oppSum(const Angle* angle) const {
3142 int lesser = SkMin32(angle->start(), angle->end());
3143 return fTs[lesser].fOppSum;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003144 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003145
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003146 int oppValue(int tIndex) const {
3147 return fTs[tIndex].fOppValue;
3148 }
3149
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003150 const SkPoint* pts() const {
3151 return fPts;
3152 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003153
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003154 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003155 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003156 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
3157 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003158 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00003159
caryclark@google.comf839c032012-10-26 21:03:50 +00003160 // This marks all spans unsortable so that this info is available for early
3161 // exclusion in find top and others. This could be optimized to only mark
3162 // adjacent spans that unsortable. However, this makes it difficult to later
3163 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003164 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003165 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003166 int angleCount = angles.count();
3167 int angleIndex;
3168 angleList.setReserve(angleCount);
3169 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003170 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00003171 *angleList.append() = &angle;
3172 sortable &= !angle.unsortable();
3173 }
3174 if (sortable) {
3175 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3176 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3177 if (angles[angleIndex].unsortable()) {
3178 sortable = false;
3179 break;
3180 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003181 }
3182 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003183 if (!sortable) {
3184 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3185 Angle& angle = angles[angleIndex];
3186 angle.segment()->markUnsortable(angle.start(), angle.end());
3187 }
3188 }
3189 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003190 }
3191
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003192 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003193 const Span& span(int tIndex) const {
3194 return fTs[tIndex];
3195 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003196
caryclark@google.com235f56a2012-09-14 14:19:30 +00003197 int spanSign(const Angle* angle) const {
3198 SkASSERT(angle->segment() == this);
3199 return spanSign(angle->start(), angle->end());
3200 }
3201
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003202 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003203 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue
3204 : fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003205#if DEBUG_WIND_BUMP
3206 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3207#endif
3208 return result;
3209 }
3210
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003211 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003212 double t(int tIndex) const {
3213 return fTs[tIndex].fT;
3214 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003215
caryclark@google.comf839c032012-10-26 21:03:50 +00003216 bool tiny(const Angle& angle) const {
3217 int start = angle.start();
3218 int end = angle.end();
3219 const Span& mSpan = fTs[SkMin32(start, end)];
3220 return mSpan.fTiny;
3221 }
3222
caryclark@google.com18063442012-07-25 12:05:18 +00003223 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3224 double start) {
3225 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003226 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003227 *outsideTs.append() = end;
3228 *outsideTs.append() = start;
3229 }
3230 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003231
caryclark@google.com24bec792012-08-20 12:43:57 +00003232 void undoneSpan(int& start, int& end) {
3233 size_t tCount = fTs.count();
3234 size_t index;
3235 for (index = 0; index < tCount; ++index) {
3236 if (!fTs[index].fDone) {
3237 break;
3238 }
3239 }
3240 SkASSERT(index < tCount - 1);
3241 start = index;
3242 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003243 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003244 SkASSERT(index < tCount);
3245 SkASSERT(index < tCount);
3246 end = index;
3247 }
caryclark@google.com18063442012-07-25 12:05:18 +00003248
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003249 bool unsortable(int index) const {
3250 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3251 }
3252
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003253 void updatePts(const SkPoint pts[]) {
3254 fPts = pts;
3255 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003256
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003257 SkPath::Verb verb() const {
3258 return fVerb;
3259 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003260
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003261 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003262 return fTs[tIndex].fWindSum;
3263 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003264
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003265 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003266 int start = angle->start();
3267 int end = angle->end();
3268 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003269 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003270 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003271
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003272 int windValue(int tIndex) const {
3273 return fTs[tIndex].fWindValue;
3274 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003275
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003276 int windValue(const Angle* angle) const {
3277 int start = angle->start();
3278 int end = angle->end();
3279 int index = SkMin32(start, end);
3280 return windValue(index);
3281 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003282
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003283 SkScalar xAtT(const Span* span) const {
3284 return xyAtT(span).fX;
3285 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003286
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003287 const SkPoint& xyAtT(int index) const {
3288 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003289 }
3290
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003291 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003292 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003293 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003294 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003295 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003296 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003297 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003298 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003299 }
3300 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003301 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003302 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003303
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003304 SkScalar yAtT(int index) const {
3305 return yAtT(&fTs[index]);
3306 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003307
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003308 SkScalar yAtT(const Span* span) const {
3309 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003310 }
3311
caryclark@google.com729e1c42012-11-21 21:36:34 +00003312 void zeroSpan(Span* span, double otherT) {
caryclark@google.com6ec15262012-11-16 20:16:50 +00003313 SkASSERT(span->fWindValue > 0);
3314 span->fWindValue = 0;
3315 if (!span->fDone) {
3316 span->fDone = true;
3317 ++fDoneSpans;
3318 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003319 int oppValue = span->fOppValue;
3320 if (!oppValue) {
3321 return;
3322 }
3323 span->fOppValue = 0;
3324 Segment* other = span->fOther;
3325 Span& oSpan = other->fTs[span->fOtherIndex];
3326 SkASSERT(0);
caryclark@google.com6ec15262012-11-16 20:16:50 +00003327 }
3328
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003329#if DEBUG_DUMP
3330 void dump() const {
3331 const char className[] = "Segment";
3332 const int tab = 4;
3333 for (int i = 0; i < fTs.count(); ++i) {
3334 SkPoint out;
3335 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3336 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003337 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003338 tab + sizeof(className), className, fID,
3339 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003340 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003341 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003342 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003343 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003344 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003345 }
3346#endif
3347
caryclark@google.com47580692012-07-23 12:14:49 +00003348#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003349 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003350 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003351 for (int i = 0; i < fTs.count(); ++i) {
3352 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3353 return;
3354 }
3355 }
3356 SkASSERT(0);
3357 }
3358#endif
3359
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003360#if DEBUG_DUMP
3361 int debugID() const {
3362 return fID;
3363 }
3364#endif
3365
caryclark@google.com24bec792012-08-20 12:43:57 +00003366#if DEBUG_WINDING
3367 void debugShowSums() const {
3368 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3369 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3370 for (int i = 0; i < fTs.count(); ++i) {
3371 const Span& span = fTs[i];
3372 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3373 if (span.fWindSum == SK_MinS32) {
3374 SkDebugf("?");
3375 } else {
3376 SkDebugf("%d", span.fWindSum);
3377 }
3378 SkDebugf("]");
3379 }
3380 SkDebugf("\n");
3381 }
3382#endif
3383
caryclark@google.comcc905052012-07-25 20:59:42 +00003384#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003385 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003386 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00003387 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00003388 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00003389 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
3390 }
3391 SkDebugf("\n");
3392 }
3393#endif
3394
caryclark@google.com027de222012-07-12 12:52:50 +00003395#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003396 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003397 if (done()) {
3398 return;
3399 }
3400 for (int i = 0; i < fTs.count(); ++i) {
3401 if (fTs[i].fDone) {
3402 continue;
3403 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003404 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003405 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3406 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3407 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3408 }
3409 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003410 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003411 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003412 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003413 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3414 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3415 if (fTs[i].fWindSum == SK_MinS32) {
3416 SkDebugf("?");
3417 } else {
3418 SkDebugf("%d", fTs[i].fWindSum);
3419 }
3420 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003421 }
3422 }
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +00003423
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003424 // This isn't useful yet -- but leaving it in for now in case i think of something
3425 // to use it for
3426 void validateActiveSpans() const {
3427 if (done()) {
3428 return;
3429 }
3430 int tCount = fTs.count();
3431 for (int index = 0; index < tCount; ++index) {
3432 if (fTs[index].fDone) {
3433 continue;
3434 }
3435 // count number of connections which are not done
3436 int first = index;
3437 double baseT = fTs[index].fT;
3438 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3439 --first;
3440 }
3441 int last = index;
3442 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3443 ++last;
3444 }
3445 int connections = 0;
3446 connections += first > 0 && !fTs[first - 1].fDone;
3447 for (int test = first; test <= last; ++test) {
3448 connections += !fTs[test].fDone;
3449 const Segment* other = fTs[test].fOther;
3450 int oIndex = fTs[test].fOtherIndex;
3451 connections += !other->fTs[oIndex].fDone;
3452 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3453 }
3454 // SkASSERT(!(connections & 1));
3455 }
3456 }
caryclark@google.com027de222012-07-12 12:52:50 +00003457#endif
3458
caryclark@google.com0c803d02012-08-06 11:15:47 +00003459#if DEBUG_MARK_DONE
3460 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3461 const SkPoint& pt = xyAtT(&span);
3462 SkDebugf("%s id=%d", fun, fID);
3463 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3464 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3465 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3466 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003467 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3468 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3469 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3470 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003471 if (span.fWindSum == SK_MinS32) {
3472 SkDebugf("?");
3473 } else {
3474 SkDebugf("%d", span.fWindSum);
3475 }
3476 SkDebugf(" windValue=%d\n", span.fWindValue);
3477 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003478
3479 void debugShowNewWinding(const char* fun, const Span& span, int winding, int oppWinding) {
3480 const SkPoint& pt = xyAtT(&span);
3481 SkDebugf("%s id=%d", fun, fID);
3482 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3483 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3484 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3485 }
3486 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3487 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3488 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d newOppSum=%d oppSum=",
3489 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
3490 winding, oppWinding);
3491 if (span.fOppSum == SK_MinS32) {
3492 SkDebugf("?");
3493 } else {
3494 SkDebugf("%d", span.fOppSum);
3495 }
3496 SkDebugf(" windSum=");
3497 if (span.fWindSum == SK_MinS32) {
3498 SkDebugf("?");
3499 } else {
3500 SkDebugf("%d", span.fWindSum);
3501 }
3502 SkDebugf(" windValue=%d\n", span.fWindValue);
3503 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003504#endif
3505
caryclark@google.com47580692012-07-23 12:14:49 +00003506#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003507 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com31143cf2012-11-09 22:14:19 +00003508 const int contourWinding, const int oppContourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003509 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003510 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003511 int lastSum = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003512 int oppLastSum = oppContourWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003513 const Angle* firstAngle = angles[first];
3514 int windSum = lastSum - spanSign(firstAngle);
3515 int oppoSign = oppSign(firstAngle);
3516 int oppWindSum = oppLastSum - oppoSign;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003517 SkDebugf("%s %s contourWinding=%d oppContourWinding=%d sign=%d\n", fun, __FUNCTION__,
3518 contourWinding, oppContourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003519 int index = first;
3520 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003521 do {
3522 const Angle& angle = *angles[index];
3523 const Segment& segment = *angle.segment();
3524 int start = angle.start();
3525 int end = angle.end();
3526 const Span& sSpan = segment.fTs[start];
3527 const Span& eSpan = segment.fTs[end];
3528 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com31143cf2012-11-09 22:14:19 +00003529 bool opp = segment.fOperand ^ fOperand;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003530 if (!firstTime) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003531 oppoSign = segment.oppSign(&angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003532 if (opp) {
3533 oppLastSum = oppWindSum;
3534 oppWindSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003535 if (oppoSign) {
3536 lastSum = windSum;
3537 windSum -= oppoSign;
3538 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003539 } else {
3540 lastSum = windSum;
3541 windSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003542 if (oppoSign) {
3543 oppLastSum = oppWindSum;
3544 oppWindSum -= oppoSign;
3545 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003546 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00003547 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003548 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 +00003549 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003550 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003551 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003552 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3553 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3554 mSpan.fWindValue);
3555 if (mSpan.fWindSum == SK_MinS32) {
3556 SkDebugf("?");
3557 } else {
3558 SkDebugf("%d", mSpan.fWindSum);
3559 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003560 int last, wind;
3561 if (opp) {
3562 last = oppLastSum;
3563 wind = oppWindSum;
3564 } else {
3565 last = lastSum;
3566 wind = windSum;
3567 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003568 if (!oppoSign) {
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003569 SkDebugf(" %d->%d (max=%d)", last, wind,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003570 useInnerWinding(last, wind) ? wind : last);
3571 } else {
3572 SkDebugf(" %d->%d (%d->%d)", last, wind, opp ? lastSum : oppLastSum,
3573 opp ? windSum : oppWindSum);
3574 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003575 SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003576#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003577 angle.debugShow(segment.xyAtT(&sSpan));
3578#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003579 ++index;
3580 if (index == angles.count()) {
3581 index = 0;
3582 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003583 if (firstTime) {
3584 firstTime = false;
3585 }
caryclark@google.com47580692012-07-23 12:14:49 +00003586 } while (index != first);
3587 }
3588#endif
3589
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003590#if DEBUG_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003591 static char as_digit(int value) {
3592 return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
3593 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003594#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003595
caryclark@google.com729e1c42012-11-21 21:36:34 +00003596#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003597 int debugShowWindingValues(int slotCount, int ofInterest) const {
3598 if (!(1 << fID & ofInterest)) {
3599 return 0;
3600 }
3601 int sum = 0;
3602 SkTDArray<char> slots;
3603 slots.setCount(slotCount * 2);
3604 memset(slots.begin(), ' ', slotCount * 2);
3605 for (int i = 0; i < fTs.count(); ++i) {
3606 // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
3607 // continue;
3608 // }
3609 sum += fTs[i].fWindValue;
3610 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
3611 sum += fTs[i].fOppValue;
3612 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
3613 }
3614 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
3615 slots.begin() + slotCount);
3616 return sum;
3617 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003618#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003619
caryclark@google.com729e1c42012-11-21 21:36:34 +00003620#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003621 bool debugVerifyWinding(int start, int end, int winding) const {
3622 const Span& span = fTs[SkMin32(start, end)];
3623 int spanWinding = span.fWindSum;
3624 if (spanWinding == SK_MinS32) {
3625 return true;
3626 }
3627 int spanSign = SkSign32(start - end);
3628 int signedVal = spanSign * span.fWindValue;
3629 if (signedVal < 0) {
3630 spanWinding -= signedVal;
3631 }
3632 return span.fWindSum == winding;
3633 }
3634#endif
3635
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003636private:
3637 const SkPoint* fPts;
3638 SkPath::Verb fVerb;
3639 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003640 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00003641 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00003642 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003643#if DEBUG_DUMP
3644 int fID;
3645#endif
3646};
3647
caryclark@google.comb9738012012-07-03 19:53:30 +00003648class Contour;
3649
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003650struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003651 Contour* fContours[2];
3652 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003653 double fTs[2][2];
3654};
3655
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003656class Contour {
3657public:
3658 Contour() {
3659 reset();
3660#if DEBUG_DUMP
3661 fID = ++gContourID;
3662#endif
3663 }
3664
3665 bool operator<(const Contour& rh) const {
3666 return fBounds.fTop == rh.fBounds.fTop
3667 ? fBounds.fLeft < rh.fBounds.fLeft
3668 : fBounds.fTop < rh.fBounds.fTop;
3669 }
3670
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003671 void addCoincident(int index, Contour* other, int otherIndex,
3672 const Intersections& ts, bool swap) {
3673 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003674 coincidence.fContours[0] = this; // FIXME: no need to store
caryclark@google.comb9738012012-07-03 19:53:30 +00003675 coincidence.fContours[1] = other;
3676 coincidence.fSegments[0] = index;
3677 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003678 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3679 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3680 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3681 coincidence.fTs[swap][0] = ts.fT[0][0];
3682 coincidence.fTs[swap][1] = ts.fT[0][1];
3683 coincidence.fTs[!swap][0] = ts.fT[1][0];
3684 coincidence.fTs[!swap][1] = ts.fT[1][1];
3685 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3686 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3687 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3688 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3689 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3690 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3691 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003692 }
3693
3694 void addCross(const Contour* crosser) {
3695#ifdef DEBUG_CROSS
3696 for (int index = 0; index < fCrosses.count(); ++index) {
3697 SkASSERT(fCrosses[index] != crosser);
3698 }
3699#endif
3700 *fCrosses.append() = crosser;
3701 }
3702
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003703 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003704 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003705 fContainsCurves = true;
3706 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003707
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003708 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003709 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003710 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003711 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003712
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003713 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3714 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3715 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003716
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003717 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003718 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003719 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003720 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003721 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003722
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003723 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3724 containsIntercepts();
3725 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3726 }
3727
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003728 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003729 return fBounds;
3730 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003731
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003732 void complete() {
3733 setBounds();
3734 fContainsIntercepts = false;
3735 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003736
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003737 void containsIntercepts() {
3738 fContainsIntercepts = true;
3739 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003740
rmistry@google.comd6176b02012-08-23 18:14:13 +00003741 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003742 int &tIndex, double& hitT) {
3743 int segmentCount = fSegments.count();
3744 const Segment* bestSegment = NULL;
3745 for (int test = 0; test < segmentCount; ++test) {
3746 Segment* testSegment = &fSegments[test];
3747 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003748 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003749 continue;
3750 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003751 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003752 continue;
3753 }
3754 if (bounds.fLeft > basePt.fX) {
3755 continue;
3756 }
3757 if (bounds.fRight < basePt.fX) {
3758 continue;
3759 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003760 if (bounds.fLeft == bounds.fRight) {
3761 continue;
3762 }
3763 #if 0
3764 bool leftHalf = bounds.fLeft == basePt.fX;
3765 bool rightHalf = bounds.fRight == basePt.fX;
3766 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3767 basePt, leftHalf, rightHalf)) {
3768 continue;
3769 }
3770 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003771 double testHitT;
3772 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3773 if (testT >= 0) {
3774 bestSegment = testSegment;
3775 tIndex = testT;
3776 hitT = testHitT;
3777 }
3778 }
3779 return bestSegment;
3780 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003781
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003782 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003783 for (int index = 0; index < fCrosses.count(); ++index) {
3784 if (fCrosses[index] == crosser) {
3785 return true;
3786 }
3787 }
3788 return false;
3789 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003790
caryclark@google.comf839c032012-10-26 21:03:50 +00003791 const SkPoint& end() const {
3792 const Segment& segment = fSegments.back();
3793 return segment.pts()[segment.verb()];
3794 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003795
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003796 void findTooCloseToCall(bool otherXor) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003797 int segmentCount = fSegments.count();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003798 otherXor ^= fXor;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003799 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003800 fSegments[sIndex].findTooCloseToCall(fXor, otherXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003801 }
3802 }
3803
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003804 void fixOtherTIndex() {
3805 int segmentCount = fSegments.count();
3806 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3807 fSegments[sIndex].fixOtherTIndex();
3808 }
3809 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003810
caryclark@google.com31143cf2012-11-09 22:14:19 +00003811 bool operand() const {
3812 return fOperand;
3813 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003814
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003815 void reset() {
3816 fSegments.reset();
3817 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003818 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003819 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003820
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003821 void resolveCoincidence(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003822 int count = fCoincidences.count();
3823 for (int index = 0; index < count; ++index) {
3824 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003825 Contour* thisContour = coincidence.fContours[0];
caryclark@google.com729e1c42012-11-21 21:36:34 +00003826 SkASSERT(thisContour == this);
caryclark@google.comb9738012012-07-03 19:53:30 +00003827 Contour* otherContour = coincidence.fContours[1];
3828 int thisIndex = coincidence.fSegments[0];
3829 int otherIndex = coincidence.fSegments[1];
3830 Segment& thisOne = thisContour->fSegments[thisIndex];
3831 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003832 #if DEBUG_CONCIDENT
3833 thisOne.debugShowTs();
3834 other.debugShowTs();
3835 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003836 double startT = coincidence.fTs[0][0];
3837 double endT = coincidence.fTs[0][1];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003838 bool cancelers = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003839 if (startT > endT) {
3840 SkTSwap<double>(startT, endT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003841 cancelers ^= true; // FIXME: just assign true
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003842 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003843 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003844 double oStartT = coincidence.fTs[1][0];
3845 double oEndT = coincidence.fTs[1][1];
3846 if (oStartT > oEndT) {
3847 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003848 cancelers ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003849 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003850 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003851 bool opp = thisContour->fOperand ^ otherContour->fOperand;
3852 if (cancelers && !opp) {
3853 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00003854 if (startT > 0 || oEndT < 1
3855 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3856 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003857 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003858 if (oStartT > 0 || endT < 1
3859 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3860 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003861 }
3862 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003863 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003864 if (startT > 0 || oStartT > 0
3865 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003866 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003867 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003868 if (endT < 1 || oEndT < 1
3869 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003870 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003871 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003872 thisOne.addTCoincident(thisContour->fXor, otherContour->fXor,
3873 startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003874 }
caryclark@google.com47580692012-07-23 12:14:49 +00003875 #if DEBUG_CONCIDENT
3876 thisOne.debugShowTs();
3877 other.debugShowTs();
3878 #endif
caryclark@google.com729e1c42012-11-21 21:36:34 +00003879 #if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003880 debugShowWindingValues(contourList);
3881 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003882 }
3883 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003884
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003885 const SkTArray<Segment>& segments() {
3886 return fSegments;
3887 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003888
caryclark@google.com235f56a2012-09-14 14:19:30 +00003889 void setOperand(bool isOp) {
3890 fOperand = isOp;
3891 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003892
caryclark@google.com235f56a2012-09-14 14:19:30 +00003893 void setXor(bool isXor) {
3894 fXor = isXor;
3895 }
3896
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003897 void sortSegments() {
3898 int segmentCount = fSegments.count();
3899 fSortedSegments.setReserve(segmentCount);
3900 for (int test = 0; test < segmentCount; ++test) {
3901 *fSortedSegments.append() = &fSegments[test];
3902 }
3903 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
3904 fFirstSorted = 0;
3905 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003906
caryclark@google.comf839c032012-10-26 21:03:50 +00003907 const SkPoint& start() const {
3908 return fSegments.front().pts()[0];
3909 }
3910
3911 void toPath(PathWrapper& path) const {
3912 int segmentCount = fSegments.count();
3913 const SkPoint& pt = fSegments.front().pts()[0];
3914 path.deferredMove(pt);
3915 for (int test = 0; test < segmentCount; ++test) {
3916 fSegments[test].addCurveTo(0, 1, path, true);
3917 }
3918 path.close();
3919 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003920
caryclark@google.comf839c032012-10-26 21:03:50 +00003921 void toPartialBackward(PathWrapper& path) const {
3922 int segmentCount = fSegments.count();
3923 for (int test = segmentCount - 1; test >= 0; --test) {
3924 fSegments[test].addCurveTo(1, 0, path, true);
3925 }
3926 }
3927
3928 void toPartialForward(PathWrapper& path) const {
3929 int segmentCount = fSegments.count();
3930 for (int test = 0; test < segmentCount; ++test) {
3931 fSegments[test].addCurveTo(0, 1, path, true);
3932 }
3933 }
3934
3935#if 0 // FIXME: obsolete, remove
caryclark@google.com15fa1382012-05-07 20:49:36 +00003936 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3937 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003938 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003939 // segments' top, and not a true sort, so it could be ameniable to regular
3940 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003941 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003942 int segmentCount = fSegments.count();
3943 SkASSERT(segmentCount > 0);
3944 int best = -1;
3945 Segment* bestSegment = NULL;
3946 while (++best < segmentCount) {
3947 Segment* testSegment = &fSegments[best];
3948 if (testSegment->done()) {
3949 continue;
3950 }
3951 bestSegment = testSegment;
3952 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003953 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003954 if (!bestSegment) {
3955 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003956 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003957 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003958 for (int test = best + 1; test < segmentCount; ++test) {
3959 Segment* testSegment = &fSegments[test];
3960 if (testSegment->done()) {
3961 continue;
3962 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003963 if (testSegment->bounds().fTop > bestTop) {
3964 continue;
3965 }
3966 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003967 if (bestTop > testTop) {
3968 bestTop = testTop;
3969 bestSegment = testSegment;
3970 }
3971 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003972 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003973 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003974 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003975#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003976
caryclark@google.comf839c032012-10-26 21:03:50 +00003977 Segment* topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003978 int segmentCount = fSortedSegments.count();
3979 SkASSERT(segmentCount > 0);
3980 Segment* bestSegment = NULL;
caryclark@google.comf839c032012-10-26 21:03:50 +00003981 int sortedIndex = fFirstSorted;
3982 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
3983 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003984 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003985 if (sortedIndex == fFirstSorted) {
3986 ++fFirstSorted;
3987 }
3988 continue;
3989 }
3990 SkPoint testXY;
3991 testSegment->activeLeftTop(testXY);
3992 if (testXY.fY < topLeft.fY) {
3993 continue;
3994 }
3995 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
3996 continue;
3997 }
3998 if (bestXY.fY < testXY.fY) {
3999 continue;
4000 }
4001 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004002 continue;
4003 }
4004 bestSegment = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00004005 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004006 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004007 return bestSegment;
4008 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004009
caryclark@google.com24bec792012-08-20 12:43:57 +00004010 Segment* undoneSegment(int& start, int& end) {
4011 int segmentCount = fSegments.count();
4012 for (int test = 0; test < segmentCount; ++test) {
4013 Segment* testSegment = &fSegments[test];
4014 if (testSegment->done()) {
4015 continue;
4016 }
4017 testSegment->undoneSpan(start, end);
4018 return testSegment;
4019 }
4020 return NULL;
4021 }
4022
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004023 int updateSegment(int index, const SkPoint* pts) {
4024 Segment& segment = fSegments[index];
4025 segment.updatePts(pts);
4026 return segment.verb() + 1;
4027 }
4028
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004029#if DEBUG_TEST
4030 SkTArray<Segment>& debugSegments() {
4031 return fSegments;
4032 }
4033#endif
4034
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004035#if DEBUG_DUMP
4036 void dump() {
4037 int i;
4038 const char className[] = "Contour";
4039 const int tab = 4;
4040 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
4041 for (i = 0; i < fSegments.count(); ++i) {
4042 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
4043 className, i);
4044 fSegments[i].dump();
4045 }
4046 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
4047 tab + sizeof(className), className,
4048 fBounds.fLeft, fBounds.fTop,
4049 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004050 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
4051 className, fContainsIntercepts);
4052 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
4053 className, fContainsCurves);
4054 }
4055#endif
4056
caryclark@google.com027de222012-07-12 12:52:50 +00004057#if DEBUG_ACTIVE_SPANS
4058 void debugShowActiveSpans() {
4059 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004060 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00004061 }
4062 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004063
4064 void validateActiveSpans() {
4065 for (int index = 0; index < fSegments.count(); ++index) {
4066 fSegments[index].validateActiveSpans();
4067 }
4068 }
caryclark@google.com027de222012-07-12 12:52:50 +00004069#endif
4070
caryclark@google.com729e1c42012-11-21 21:36:34 +00004071#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004072 int debugShowWindingValues(int totalSegments, int ofInterest) {
4073 int count = fSegments.count();
4074 int sum = 0;
4075 for (int index = 0; index < count; ++index) {
4076 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
4077 }
4078 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
4079 return sum;
4080 }
4081
4082 static void debugShowWindingValues(SkTDArray<Contour*>& contourList) {
4083 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
4084 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
4085 int ofInterest = 1 << 5 | 1 << 8;
4086 int total = 0;
4087 int index;
4088 for (index = 0; index < contourList.count(); ++index) {
4089 total += contourList[index]->segments().count();
4090 }
4091 int sum = 0;
4092 for (index = 0; index < contourList.count(); ++index) {
4093 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
4094 }
4095 // SkDebugf("%s total=%d\n", __FUNCTION__, sum);
4096 }
4097#endif
4098
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004099protected:
4100 void setBounds() {
4101 int count = fSegments.count();
4102 if (count == 0) {
4103 SkDebugf("%s empty contour\n", __FUNCTION__);
4104 SkASSERT(0);
4105 // FIXME: delete empty contour?
4106 return;
4107 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004108 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004109 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004110 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004111 }
4112 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004113
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004114private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004115 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004116 SkTDArray<Segment*> fSortedSegments;
4117 int fFirstSorted;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004118 SkTDArray<Coincidence> fCoincidences;
4119 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004120 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004121 bool fContainsIntercepts;
4122 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004123 bool fOperand; // true for the second argument to a binary operator
4124 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004125#if DEBUG_DUMP
4126 int fID;
4127#endif
4128};
4129
4130class EdgeBuilder {
4131public:
4132
caryclark@google.comf839c032012-10-26 21:03:50 +00004133EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
4134 : fPath(path.nativePath())
4135 , fContours(contours)
4136{
4137 init();
4138}
4139
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004140EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00004141 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004142 , fContours(contours)
4143{
caryclark@google.comf839c032012-10-26 21:03:50 +00004144 init();
4145}
4146
4147void init() {
4148 fCurrentContour = NULL;
4149 fOperand = false;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004150 fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004151#if DEBUG_DUMP
4152 gContourID = 0;
4153 gSegmentID = 0;
4154#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00004155 fSecondHalf = preFetch();
4156}
4157
4158void addOperand(const SkPath& path) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004159 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
4160 fPathVerbs.pop();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004161 fPath = &path;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004162 fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004163 preFetch();
4164}
4165
4166void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004167 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004168 complete();
4169 if (fCurrentContour && !fCurrentContour->segments().count()) {
4170 fContours.pop_back();
4171 }
4172 // correct pointers in contours since fReducePts may have moved as it grew
4173 int cIndex = 0;
4174 int extraCount = fExtra.count();
4175 SkASSERT(extraCount == 0 || fExtra[0] == -1);
4176 int eIndex = 0;
4177 int rIndex = 0;
4178 while (++eIndex < extraCount) {
4179 int offset = fExtra[eIndex];
4180 if (offset < 0) {
4181 ++cIndex;
4182 continue;
4183 }
4184 fCurrentContour = &fContours[cIndex];
4185 rIndex += fCurrentContour->updateSegment(offset - 1,
4186 &fReducePts[rIndex]);
4187 }
4188 fExtra.reset(); // we're done with this
4189}
4190
4191ShapeOpMask xorMask() const {
caryclark@google.com729e1c42012-11-21 21:36:34 +00004192 return fXorMask[fOperand];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004193}
4194
4195protected:
4196
4197void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004198 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004199 fCurrentContour->complete();
4200 fCurrentContour = NULL;
4201 }
4202}
4203
caryclark@google.com235f56a2012-09-14 14:19:30 +00004204// FIXME:remove once we can access path pts directly
4205int preFetch() {
4206 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004207 SkPoint pts[4];
4208 SkPath::Verb verb;
4209 do {
4210 verb = iter.next(pts);
4211 *fPathVerbs.append() = verb;
4212 if (verb == SkPath::kMove_Verb) {
4213 *fPathPts.append() = pts[0];
4214 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
4215 fPathPts.append(verb, &pts[1]);
4216 }
4217 } while (verb != SkPath::kDone_Verb);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004218 return fPathVerbs.count() - 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004219}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004220
caryclark@google.com235f56a2012-09-14 14:19:30 +00004221void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004222 SkPath::Verb reducedVerb;
4223 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004224 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004225 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004226 const SkPoint* finalCurveStart = NULL;
4227 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004228 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004229 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
4230 switch (verb) {
4231 case SkPath::kMove_Verb:
4232 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004233 if (!fCurrentContour) {
4234 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004235 fCurrentContour->setOperand(fOperand);
caryclark@google.com729e1c42012-11-21 21:36:34 +00004236 fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004237 *fExtra.append() = -1; // start new contour
4238 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004239 finalCurveEnd = pointsPtr++;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004240 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004241 case SkPath::kLine_Verb:
4242 // skip degenerate points
4243 if (pointsPtr[-1].fX != pointsPtr[0].fX
4244 || pointsPtr[-1].fY != pointsPtr[0].fY) {
4245 fCurrentContour->addLine(&pointsPtr[-1]);
4246 }
4247 break;
4248 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00004249
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004250 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
4251 if (reducedVerb == 0) {
4252 break; // skip degenerate points
4253 }
4254 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00004255 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004256 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004257 break;
4258 }
4259 fCurrentContour->addQuad(&pointsPtr[-1]);
4260 break;
4261 case SkPath::kCubic_Verb:
4262 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
4263 if (reducedVerb == 0) {
4264 break; // skip degenerate points
4265 }
4266 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004267 *fExtra.append() =
4268 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004269 break;
4270 }
4271 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004272 *fExtra.append() =
4273 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004274 break;
4275 }
4276 fCurrentContour->addCubic(&pointsPtr[-1]);
4277 break;
4278 case SkPath::kClose_Verb:
4279 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004280 if (finalCurveStart && finalCurveEnd
4281 && *finalCurveStart != *finalCurveEnd) {
4282 *fReducePts.append() = *finalCurveStart;
4283 *fReducePts.append() = *finalCurveEnd;
4284 *fExtra.append() =
4285 fCurrentContour->addLine(fReducePts.end() - 2);
4286 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004287 complete();
caryclark@google.com31143cf2012-11-09 22:14:19 +00004288 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004289 default:
4290 SkDEBUGFAIL("bad verb");
4291 return;
4292 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004293 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004294 pointsPtr += verb;
4295 SkASSERT(fCurrentContour);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004296 nextVerb:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004297 if (verbPtr == endOfFirstHalf) {
4298 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004299 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004300 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004301}
4302
4303private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004304 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004305 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004306 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004307 Contour* fCurrentContour;
4308 SkTArray<Contour>& fContours;
4309 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004310 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com729e1c42012-11-21 21:36:34 +00004311 ShapeOpMask fXorMask[2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004312 int fSecondHalf;
4313 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004314};
4315
4316class Work {
4317public:
4318 enum SegmentType {
4319 kHorizontalLine_Segment = -1,
4320 kVerticalLine_Segment = 0,
4321 kLine_Segment = SkPath::kLine_Verb,
4322 kQuad_Segment = SkPath::kQuad_Verb,
4323 kCubic_Segment = SkPath::kCubic_Verb,
4324 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004325
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004326 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4327 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4328 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004329
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004330 // FIXME: does it make sense to write otherIndex now if we're going to
4331 // fix it up later?
4332 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004333 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004334 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004335
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004336 // Avoid collapsing t values that are close to the same since
4337 // we walk ts to describe consecutive intersections. Since a pair of ts can
4338 // be nearly equal, any problems caused by this should be taken care
4339 // of later.
4340 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004341 int addT(double newT, const Work& other) {
4342 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004343 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004344
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004345 bool advance() {
4346 return ++fIndex < fLast;
4347 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004348
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004349 SkScalar bottom() const {
4350 return bounds().fBottom;
4351 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004352
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004353 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004354 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004355 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004356
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004357 const SkPoint* cubic() const {
4358 return fCubic;
4359 }
4360
4361 void init(Contour* contour) {
4362 fContour = contour;
4363 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004364 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004365 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004366
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004367 bool isAdjacent(const Work& next) {
4368 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4369 }
4370
4371 bool isFirstLast(const Work& next) {
4372 return fContour == next.fContour && fIndex == 0
4373 && next.fIndex == fLast - 1;
4374 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004375
4376 SkScalar left() const {
4377 return bounds().fLeft;
4378 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004379
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004380 void promoteToCubic() {
4381 fCubic[0] = pts()[0];
4382 fCubic[2] = pts()[1];
4383 fCubic[3] = pts()[2];
4384 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4385 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4386 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4387 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4388 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004389
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004390 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004391 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004392 }
4393
4394 SkScalar right() const {
4395 return bounds().fRight;
4396 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004397
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004398 ptrdiff_t segmentIndex() const {
4399 return fIndex;
4400 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004401
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004402 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004403 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004404 SegmentType type = (SegmentType) segment.verb();
4405 if (type != kLine_Segment) {
4406 return type;
4407 }
4408 if (segment.isHorizontal()) {
4409 return kHorizontalLine_Segment;
4410 }
4411 if (segment.isVertical()) {
4412 return kVerticalLine_Segment;
4413 }
4414 return kLine_Segment;
4415 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004416
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004417 bool startAfter(const Work& after) {
4418 fIndex = after.fIndex;
4419 return advance();
4420 }
4421
4422 SkScalar top() const {
4423 return bounds().fTop;
4424 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004425
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004426 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004427 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004428 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004429
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004430 SkScalar x() const {
4431 return bounds().fLeft;
4432 }
4433
4434 bool xFlipped() const {
4435 return x() != pts()[0].fX;
4436 }
4437
4438 SkScalar y() const {
4439 return bounds().fTop;
4440 }
4441
4442 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004443 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004444 }
4445
4446protected:
4447 Contour* fContour;
4448 SkPoint fCubic[4];
4449 int fIndex;
4450 int fLast;
4451};
4452
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004453#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004454static void debugShowLineIntersection(int pts, const Work& wt,
4455 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004456 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004457 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004458 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4459 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4460 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4461 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004462 return;
4463 }
4464 SkPoint wtOutPt, wnOutPt;
4465 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4466 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004467 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 +00004468 __FUNCTION__,
4469 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4470 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4471 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004472 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004473 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004474 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004475 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4476 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4477 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004478 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4479 }
4480 SkDebugf("\n");
4481}
4482
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004483static void debugShowQuadLineIntersection(int pts, const Work& wt,
4484 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4485 if (!pts) {
4486 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00004487 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004488 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4489 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004490 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004491 return;
4492 }
4493 SkPoint wtOutPt, wnOutPt;
4494 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4495 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4496 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4497 __FUNCTION__,
4498 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4499 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4500 wtOutPt.fX, wtOutPt.fY);
4501 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004502 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
4503 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004504 }
4505 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4506 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4507 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4508 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004509 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
4510 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004511 }
4512 SkDebugf("\n");
4513}
4514
caryclark@google.coma461ff02012-10-11 12:54:23 +00004515static void debugShowQuadIntersection(int pts, const Work& wt,
4516 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4517 if (!pts) {
4518 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
4519 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
4520 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00004521 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00004522 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004523 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00004524 return;
4525 }
4526 SkPoint wtOutPt, wnOutPt;
4527 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4528 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4529 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4530 __FUNCTION__,
4531 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4532 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4533 wtOutPt.fX, wtOutPt.fY);
4534 if (pts == 2) {
4535 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
4536 }
4537 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4538 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4539 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
4540 wnOutPt.fX, wnOutPt.fY);
4541 if (pts == 2) {
4542 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004543 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004544 SkDebugf("\n");
4545}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004546#else
4547static void debugShowLineIntersection(int , const Work& ,
4548 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004549}
caryclark@google.coma461ff02012-10-11 12:54:23 +00004550
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004551static void debugShowQuadLineIntersection(int , const Work& ,
4552 const Work& , const double [2], const double [2]) {
4553}
4554
caryclark@google.coma461ff02012-10-11 12:54:23 +00004555static void debugShowQuadIntersection(int , const Work& ,
4556 const Work& , const double [2], const double [2]) {
4557}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004558#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004559
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004560static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004561
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004562 if (test != next) {
4563 if (test->bounds().fBottom < next->bounds().fTop) {
4564 return false;
4565 }
4566 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
4567 return true;
4568 }
4569 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004570 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004571 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004572 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004573 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004574 Work wn;
4575 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004576 if (test == next && !wn.startAfter(wt)) {
4577 continue;
4578 }
4579 do {
4580 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
4581 continue;
4582 }
4583 int pts;
4584 Intersections ts;
4585 bool swap = false;
4586 switch (wt.segmentType()) {
4587 case Work::kHorizontalLine_Segment:
4588 swap = true;
4589 switch (wn.segmentType()) {
4590 case Work::kHorizontalLine_Segment:
4591 case Work::kVerticalLine_Segment:
4592 case Work::kLine_Segment: {
4593 pts = HLineIntersect(wn.pts(), wt.left(),
4594 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004595 debugShowLineIntersection(pts, wt, wn,
4596 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004597 break;
4598 }
4599 case Work::kQuad_Segment: {
4600 pts = HQuadIntersect(wn.pts(), wt.left(),
4601 wt.right(), wt.y(), wt.xFlipped(), ts);
4602 break;
4603 }
4604 case Work::kCubic_Segment: {
4605 pts = HCubicIntersect(wn.pts(), wt.left(),
4606 wt.right(), wt.y(), wt.xFlipped(), ts);
4607 break;
4608 }
4609 default:
4610 SkASSERT(0);
4611 }
4612 break;
4613 case Work::kVerticalLine_Segment:
4614 swap = true;
4615 switch (wn.segmentType()) {
4616 case Work::kHorizontalLine_Segment:
4617 case Work::kVerticalLine_Segment:
4618 case Work::kLine_Segment: {
4619 pts = VLineIntersect(wn.pts(), wt.top(),
4620 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004621 debugShowLineIntersection(pts, wt, wn,
4622 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004623 break;
4624 }
4625 case Work::kQuad_Segment: {
4626 pts = VQuadIntersect(wn.pts(), wt.top(),
4627 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4628 break;
4629 }
4630 case Work::kCubic_Segment: {
4631 pts = VCubicIntersect(wn.pts(), wt.top(),
4632 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4633 break;
4634 }
4635 default:
4636 SkASSERT(0);
4637 }
4638 break;
4639 case Work::kLine_Segment:
4640 switch (wn.segmentType()) {
4641 case Work::kHorizontalLine_Segment:
4642 pts = HLineIntersect(wt.pts(), wn.left(),
4643 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004644 debugShowLineIntersection(pts, wt, wn,
4645 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004646 break;
4647 case Work::kVerticalLine_Segment:
4648 pts = VLineIntersect(wt.pts(), wn.top(),
4649 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004650 debugShowLineIntersection(pts, wt, wn,
4651 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004652 break;
4653 case Work::kLine_Segment: {
4654 pts = LineIntersect(wt.pts(), wn.pts(), ts);
4655 debugShowLineIntersection(pts, wt, wn,
4656 ts.fT[1], ts.fT[0]);
4657 break;
4658 }
4659 case Work::kQuad_Segment: {
4660 swap = true;
4661 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004662 debugShowQuadLineIntersection(pts, wn, wt,
4663 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004664 break;
4665 }
4666 case Work::kCubic_Segment: {
4667 swap = true;
4668 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
4669 break;
4670 }
4671 default:
4672 SkASSERT(0);
4673 }
4674 break;
4675 case Work::kQuad_Segment:
4676 switch (wn.segmentType()) {
4677 case Work::kHorizontalLine_Segment:
4678 pts = HQuadIntersect(wt.pts(), wn.left(),
4679 wn.right(), wn.y(), wn.xFlipped(), ts);
4680 break;
4681 case Work::kVerticalLine_Segment:
4682 pts = VQuadIntersect(wt.pts(), wn.top(),
4683 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4684 break;
4685 case Work::kLine_Segment: {
4686 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004687 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004688 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004689 break;
4690 }
4691 case Work::kQuad_Segment: {
4692 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004693 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004694 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004695 break;
4696 }
4697 case Work::kCubic_Segment: {
4698 wt.promoteToCubic();
4699 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
4700 break;
4701 }
4702 default:
4703 SkASSERT(0);
4704 }
4705 break;
4706 case Work::kCubic_Segment:
4707 switch (wn.segmentType()) {
4708 case Work::kHorizontalLine_Segment:
4709 pts = HCubicIntersect(wt.pts(), wn.left(),
4710 wn.right(), wn.y(), wn.xFlipped(), ts);
4711 break;
4712 case Work::kVerticalLine_Segment:
4713 pts = VCubicIntersect(wt.pts(), wn.top(),
4714 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4715 break;
4716 case Work::kLine_Segment: {
4717 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
4718 break;
4719 }
4720 case Work::kQuad_Segment: {
4721 wn.promoteToCubic();
4722 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
4723 break;
4724 }
4725 case Work::kCubic_Segment: {
4726 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4727 break;
4728 }
4729 default:
4730 SkASSERT(0);
4731 }
4732 break;
4733 default:
4734 SkASSERT(0);
4735 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004736 if (!foundCommonContour && pts > 0) {
4737 test->addCross(next);
4738 next->addCross(test);
4739 foundCommonContour = true;
4740 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004741 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004742 if (pts == 2) {
4743 if (wn.segmentType() <= Work::kLine_Segment
4744 && wt.segmentType() <= Work::kLine_Segment) {
4745 wt.addCoincident(wn, ts, swap);
4746 continue;
4747 }
4748 if (wn.segmentType() == Work::kQuad_Segment
4749 && wt.segmentType() == Work::kQuad_Segment
4750 && ts.coincidentUsed() == 2) {
4751 wt.addCoincident(wn, ts, swap);
4752 continue;
4753 }
4754
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004755 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004756 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004757 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4758 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004759 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4760 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004761 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4762 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004763 }
4764 } while (wn.advance());
4765 } while (wt.advance());
4766 return true;
4767}
4768
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004769// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004770// see if coincidence is formed by clipping non-concident segments
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004771static void coincidenceCheck(SkTDArray<Contour*>& contourList, bool otherXor, int total) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004772 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004773 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004774 Contour* contour = contourList[cIndex];
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004775 contour->resolveCoincidence(contourList);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004776 }
4777 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4778 Contour* contour = contourList[cIndex];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004779 contour->findTooCloseToCall(otherXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004780 }
4781}
4782
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004783// project a ray from the top of the contour up and see if it hits anything
4784// note: when we compute line intersections, we keep track of whether
4785// two contours touch, so we need only look at contours not touching this one.
4786// OPTIMIZATION: sort contourList vertically to avoid linear walk
4787static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com31143cf2012-11-09 22:14:19 +00004788 const Segment* current, int index, int endIndex, bool opp) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004789 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004790 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004791 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00004792 const Segment* test = NULL;
4793 int tIndex;
4794 double tHit;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004795 for (int cTest = 0; cTest < contourCount; ++cTest) {
4796 Contour* contour = contourList[cTest];
caryclark@google.com31143cf2012-11-09 22:14:19 +00004797 if (contour->operand() ^ current->operand() != opp) {
4798 continue;
4799 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004800 if (basePt.fY < contour->bounds().fTop) {
4801 continue;
4802 }
4803 if (bestY > contour->bounds().fBottom) {
4804 continue;
4805 }
caryclark@google.com47580692012-07-23 12:14:49 +00004806 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
4807 if (next) {
4808 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004809 }
caryclark@google.com47580692012-07-23 12:14:49 +00004810 }
4811 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00004812 return 0;
4813 }
4814 int winding, windValue;
4815 // If the ray hit the end of a span, we need to construct the wheel of
4816 // angles to find the span closest to the ray -- even if there are just
4817 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004818 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004819 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00004820 SkTDArray<Angle> angles;
4821 int end = test->nextSpan(tIndex, 1);
4822 if (end < 0) {
4823 end = test->nextSpan(tIndex, -1);
4824 }
4825 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00004826 SkASSERT(angles.count() > 0);
4827 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
4828#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00004829 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00004830#endif
4831 return 0;
4832 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004833 test->buildAngles(tIndex, angles, false);
caryclark@google.com47580692012-07-23 12:14:49 +00004834 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00004835 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00004836 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004837 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00004838 // hour after 6 o'clock
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004839 (void) Segment::SortAngles(angles, sorted);
caryclark@google.com47580692012-07-23 12:14:49 +00004840#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004841 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00004842#endif
4843 // walk the sorted angle fan to find the lowest angle
4844 // above the base point. Currently, the first angle in the sorted array
4845 // is 12 noon or an earlier hour (the next counterclockwise)
4846 int count = sorted.count();
4847 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004848 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00004849 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004850 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00004851 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00004852 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004853 if (angle->unsortable()) {
4854 continue;
4855 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004856 if (baseMatches && angle->isHorizontal()) {
4857 continue;
4858 }
4859 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00004860 test = angle->segment();
4861 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
4862 const SkPoint* pts = test->pts();
4863 indexDx = pts[2].fX - pts[1].fX - indexDx;
4864 }
caryclark@google.com47580692012-07-23 12:14:49 +00004865 if (indexDx < 0) {
4866 left = index;
4867 } else if (indexDx > 0) {
4868 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00004869 int previous = index - 1;
4870 if (previous < 0) {
4871 previous = count - 1;
4872 }
4873 const Angle* prev = sorted[previous];
4874 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
4875#if DEBUG_SORT
4876 SkDebugf("%s use prev\n", __FUNCTION__);
4877#endif
4878 right = previous;
4879 }
caryclark@google.com47580692012-07-23 12:14:49 +00004880 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004881 } else {
4882 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004883 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004884 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004885 if (left < 0 && right < 0) {
4886 left = mid;
4887 }
caryclark@google.com47580692012-07-23 12:14:49 +00004888 SkASSERT(left >= 0 || right >= 0);
4889 if (left < 0) {
4890 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004891 } else if (left >= 0 && mid >= 0 && right >= 0
4892 && sorted[mid]->sign() == sorted[right]->sign()) {
4893 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004894 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004895 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004896 test = angle->segment();
4897 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004898 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004899 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004900#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004901 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4902 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004903#endif
4904 } else {
4905 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004906 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004907 windValue = test->windValue(tIndex);
4908#if DEBUG_WINDING
4909 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4910 windValue);
4911#endif
4912 }
4913 // see if a + change in T results in a +/- change in X (compute x'(T))
4914 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004915 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4916 const SkPoint* pts = test->pts();
4917 dx = pts[2].fX - pts[1].fX - dx;
4918 }
caryclark@google.com47580692012-07-23 12:14:49 +00004919#if DEBUG_WINDING
4920 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4921#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004922 SkASSERT(dx != 0);
4923 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004924 winding += dx > 0 ? -windValue : windValue;
4925#if DEBUG_WINDING
4926 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4927#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004928 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004929 return winding;
4930}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004931
caryclark@google.com24bec792012-08-20 12:43:57 +00004932static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4933 int contourCount = contourList.count();
4934 Segment* result;
4935 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4936 Contour* contour = contourList[cIndex];
4937 result = contour->undoneSegment(start, end);
4938 if (result) {
4939 return result;
4940 }
4941 }
4942 return NULL;
4943}
4944
4945
4946
caryclark@google.com31143cf2012-11-09 22:14:19 +00004947static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004948 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004949 Span* span;
4950 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004951 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4952 Segment* segment = backPtr.fOther;
4953 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004954 SkTDArray<Angle> angles;
4955 int done = 0;
4956 if (segment->activeAngle(tIndex, done, angles)) {
4957 Angle* last = angles.end() - 1;
4958 tIndex = last->start();
4959 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00004960 #if TRY_ROTATE
4961 *chase.insert(0) = span;
4962 #else
4963 *chase.append() = span;
4964 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004965 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004966 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004967 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004968 continue;
4969 }
4970 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004971 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004972#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004973 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004974#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004975 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004976 continue;
4977 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004978 // find first angle, initialize winding to computed fWindSum
4979 int firstIndex = -1;
4980 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004981 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004982 do {
4983 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004984 segment = angle->segment();
4985 winding = segment->windSum(angle);
4986 } while (winding == SK_MinS32);
4987 int spanWinding = segment->spanSign(angle->start(), angle->end());
4988 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00004989 SkDebugf("%s winding=%d spanWinding=%d\n",
4990 __FUNCTION__, winding, spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00004991 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00004992 // turn span winding into contour winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004993 if (spanWinding * winding < 0) {
4994 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004995 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004996 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004997 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004998 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004999 // we care about first sign and whether wind sum indicates this
5000 // edge is inside or outside. Maybe need to pass span winding
5001 // or first winding or something into this function?
5002 // advance to first undone angle, then return it and winding
5003 // (to set whether edges are active or not)
5004 int nextIndex = firstIndex + 1;
5005 int angleCount = sorted.count();
5006 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005007 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00005008 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00005009 do {
5010 SkASSERT(nextIndex != firstIndex);
5011 if (nextIndex == angleCount) {
5012 nextIndex = 0;
5013 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005014 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00005015 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005016 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00005017 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005018 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00005019 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
5020 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005021 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005022 tIndex = angle->start();
5023 endIndex = angle->end();
5024 int lesser = SkMin32(tIndex, endIndex);
5025 const Span& nextSpan = segment->span(lesser);
5026 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005027#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00005028 // FIXME: this be wrong. assign startWinding if edge is in
5029 // same direction. If the direction is opposite, winding to
5030 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00005031 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005032 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005033 }
caryclark@google.com59823f72012-08-09 18:17:47 +00005034 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005035#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005036 break;
5037 }
5038 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005039 #if TRY_ROTATE
5040 *chase.insert(0) = span;
5041 #else
5042 *chase.append() = span;
5043 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005044 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005045 }
5046 return NULL;
5047}
5048
caryclark@google.com027de222012-07-12 12:52:50 +00005049#if DEBUG_ACTIVE_SPANS
5050static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005051 int index;
5052 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00005053 contourList[index]->debugShowActiveSpans();
5054 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005055 for (index = 0; index < contourList.count(); ++ index) {
5056 contourList[index]->validateActiveSpans();
5057 }
caryclark@google.com027de222012-07-12 12:52:50 +00005058}
5059#endif
5060
caryclark@google.com27c449a2012-07-27 18:26:38 +00005061static bool windingIsActive(int winding, int spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005062 // FIXME: !spanWinding test must be superflorous, true?
caryclark@google.com27c449a2012-07-27 18:26:38 +00005063 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
5064 && (!winding || !spanWinding || winding == -spanWinding);
5065}
5066
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005067static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comf839c032012-10-26 21:03:50 +00005068 int& endIndex, SkPoint& topLeft) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005069 Segment* result;
5070 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00005071 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005072 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00005073 Segment* topStart = NULL;
5074 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5075 Contour* contour = contourList[cIndex];
5076 const Bounds& bounds = contour->bounds();
5077 if (bounds.fBottom < topLeft.fY) {
5078 continue;
5079 }
5080 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
5081 continue;
5082 }
5083 Segment* test = contour->topSortableSegment(topLeft, bestXY);
5084 if (test) {
5085 topStart = test;
5086 }
5087 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005088 if (!topStart) {
5089 return NULL;
5090 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005091 topLeft = bestXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005092 result = topStart->findTop(index, endIndex);
5093 } while (!result);
5094 return result;
5095}
caryclark@google.com31143cf2012-11-09 22:14:19 +00005096
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005097static int updateWindings(const Segment* current, int index, int endIndex, int& spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005098 int lesser = SkMin32(index, endIndex);
5099 spanWinding = current->spanSign(index, endIndex);
5100 int winding = current->windSum(lesser);
5101 bool inner = useInnerWinding(winding - spanWinding, winding);
5102#if DEBUG_WINDING
5103 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
5104 " inner=%d result=%d\n",
5105 __FUNCTION__, current->debugID(), current->t(lesser),
5106 spanWinding, winding, SkSign32(index - endIndex),
5107 useInnerWinding(winding - spanWinding, winding),
5108 inner ? winding - spanWinding : winding);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005109#endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005110 if (inner) {
5111 winding -= spanWinding;
5112 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005113 return winding;
5114}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005115
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005116// Each segment may have an inside or an outside. Segments contained within
5117// winding may have insides on either side, and form a contour that should be
5118// ignored. Segments that are coincident with opposing direction segments may
5119// have outsides on either side, and should also disappear.
5120// 'Normal' segments will have one inside and one outside. Subsequent connections
5121// when winding should follow the intersection direction. If more than one edge
5122// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005123 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00005124 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005125// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005126static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005127 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005128 bool unsortable = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005129 bool closable = true;
5130 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com15fa1382012-05-07 20:49:36 +00005131 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005132 int index, endIndex;
caryclark@google.com31143cf2012-11-09 22:14:19 +00005133 // iterates while top is unsortable
caryclark@google.comf839c032012-10-26 21:03:50 +00005134 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005135 if (!current) {
5136 break;
5137 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005138 int contourWinding;
5139 if (firstContour) {
5140 contourWinding = 0;
5141 firstContour = false;
5142 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00005143 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005144 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00005145 if (sumWinding == SK_MinS32) {
caryclark@google.com6ec15262012-11-16 20:16:50 +00005146 sumWinding = current->computeSum(index, endIndex, NULL);
caryclark@google.com200c2112012-08-03 15:05:04 +00005147 }
5148 if (sumWinding == SK_MinS32) {
5149 contourWinding = innerContourCheck(contourList, current,
caryclark@google.com31143cf2012-11-09 22:14:19 +00005150 index, endIndex, false);
caryclark@google.com200c2112012-08-03 15:05:04 +00005151 } else {
5152 contourWinding = sumWinding;
5153 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00005154 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
5155 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00005156 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005157 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00005158#if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005159 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n",
5160 __FUNCTION__, sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00005161 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00005162#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005163 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005164#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005165 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005166 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
5167#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005168 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005169 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005170 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005171 // FIXME: needs work. While it works in limited situations, it does
5172 // not always compute winding correctly. Active should be removed and instead
5173 // the initial winding should be correctly passed in so that if the
5174 // inner contour is wound the same way, it never finds an accumulated
5175 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00005176 // other than zero when resolving sorted angles.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005177 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005178 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005179 bool active = windingIsActive(winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005180 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00005181 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
5182 __FUNCTION__, active ? "true" : "false",
5183 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005184 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005185 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005186 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005187 int nextStart = index;
5188 int nextEnd = endIndex;
5189 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005190 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005191 if (!next) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005192 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00005193 && current->verb() != SkPath::kLine_Verb
5194 && !simple.isClosed()) {
5195 current->addCurveTo(index, endIndex, simple, true);
5196 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005197 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005198 break;
5199 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005200 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005201 current = next;
5202 index = nextStart;
5203 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005204 } while (!simple.isClosed()
5205 && ((active && !unsortable) || !current->done()));
5206 if (active) {
5207 if (!simple.isClosed()) {
5208 SkASSERT(unsortable);
5209 int min = SkMin32(index, endIndex);
5210 if (!current->done(min)) {
5211 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.com31143cf2012-11-09 22:14:19 +00005212 current->markDone(SkMin32(index, endIndex),
5213 winding ? winding : spanWinding);
caryclark@google.comf839c032012-10-26 21:03:50 +00005214 }
5215 closable = false;
5216 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005217 simple.close();
5218 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005219 current = findChase(chaseArray, index, endIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005220 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00005221 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005222 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005223 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005224 break;
5225 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005226 winding = updateWindings(current, index, endIndex, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005227 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005228 } while (true);
caryclark@google.comf839c032012-10-26 21:03:50 +00005229 return closable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005230}
5231
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005232// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005233static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005234 Segment* current;
5235 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005236 bool unsortable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00005237 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005238 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005239 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005240 int nextStart = start;
5241 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005242 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00005243 if (!next) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005244 if (simple.hasMove()
5245 && current->verb() != SkPath::kLine_Verb
5246 && !simple.isClosed()) {
5247 current->addCurveTo(start, end, simple, true);
5248 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005249 }
caryclark@google.com24bec792012-08-20 12:43:57 +00005250 break;
5251 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005252 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00005253 current = next;
5254 start = nextStart;
5255 end = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005256 } while (!simple.isClosed());
5257 // FIXME: add unsortable test
5258 if (simple.hasMove()) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005259 simple.close();
5260 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005261 #if DEBUG_ACTIVE_SPANS
5262 debugShowActiveSpans(contourList);
5263 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005264 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005265 return !unsortable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005266}
5267
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005268static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5269 int contourCount = contourList.count();
5270 for (int cTest = 0; cTest < contourCount; ++cTest) {
5271 Contour* contour = contourList[cTest];
5272 contour->fixOtherTIndex();
5273 }
5274}
5275
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005276static void sortSegments(SkTDArray<Contour*>& contourList) {
5277 int contourCount = contourList.count();
5278 for (int cTest = 0; cTest < contourCount; ++cTest) {
5279 Contour* contour = contourList[cTest];
5280 contour->sortSegments();
5281 }
5282}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005283
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005284static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005285 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005286 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005287 if (count == 0) {
5288 return;
5289 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005290 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005291 *list.append() = &contours[index];
5292 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005293 QSort<Contour>(list.begin(), list.end() - 1);
5294}
5295
caryclark@google.comf839c032012-10-26 21:03:50 +00005296static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005297 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005298}
5299
caryclark@google.comf839c032012-10-26 21:03:50 +00005300 /*
5301 check start and end of each contour
5302 if not the same, record them
5303 match them up
5304 connect closest
5305 reassemble contour pieces into new path
5306 */
5307static void assemble(const PathWrapper& path, PathWrapper& simple) {
5308#if DEBUG_PATH_CONSTRUCTION
5309 SkDebugf("%s\n", __FUNCTION__);
5310#endif
5311 SkTArray<Contour> contours;
5312 EdgeBuilder builder(path, contours);
5313 builder.finish();
5314 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005315 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005316 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00005317 for (outer = 0; outer < count; ++outer) {
5318 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005319 const SkPoint& eStart = eContour.start();
5320 const SkPoint& eEnd = eContour.end();
5321 if (approximatelyEqual(eStart, eEnd)) {
5322 eContour.toPath(simple);
5323 continue;
5324 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005325 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005326 }
5327 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005328 if (count == 0) {
5329 return;
5330 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005331 SkTDArray<int> sLink, eLink;
5332 sLink.setCount(count);
5333 eLink.setCount(count);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005334 SkTDArray<double> sBest, eBest;
5335 sBest.setCount(count);
5336 eBest.setCount(count);
caryclark@google.comf839c032012-10-26 21:03:50 +00005337 int rIndex;
5338 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005339 outer = runs[rIndex];
5340 const Contour& oContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005341 const SkPoint& oStart = oContour.start();
5342 const SkPoint& oEnd = oContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005343 double dx = oEnd.fX - oStart.fX;
5344 double dy = oEnd.fY - oStart.fY;
5345 double dist = dx * dx + dy * dy;
5346 sBest[rIndex] = eBest[rIndex] = dist;
5347 sLink[rIndex] = eLink[rIndex] = rIndex;
5348 }
5349 for (rIndex = 0; rIndex < count - 1; ++rIndex) {
5350 outer = runs[rIndex];
5351 const Contour& oContour = contours[outer];
5352 const SkPoint& oStart = oContour.start();
5353 const SkPoint& oEnd = oContour.end();
5354 double bestStartDist = sBest[rIndex];
5355 double bestEndDist = eBest[rIndex];
5356 for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) {
5357 int inner = runs[iIndex];
5358 const Contour& iContour = contours[inner];
caryclark@google.comf839c032012-10-26 21:03:50 +00005359 const SkPoint& iStart = iContour.start();
5360 const SkPoint& iEnd = iContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005361 double dx = iStart.fX - oStart.fX;
5362 double dy = iStart.fY - oStart.fY;
5363 double dist = dx * dx + dy * dy;
5364 if (bestStartDist > dist) {
5365 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005366 sLink[rIndex] = ~iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005367 sLink[iIndex] = ~rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005368 }
5369 dx = iEnd.fX - oStart.fX;
5370 dy = iEnd.fY - oStart.fY;
5371 dist = dx * dx + dy * dy;
5372 if (bestStartDist > dist) {
5373 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005374 sLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005375 eLink[iIndex] = rIndex;
5376 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005377 dx = iStart.fX - oEnd.fX;
5378 dy = iStart.fY - oEnd.fY;
5379 dist = dx * dx + dy * dy;
5380 if (bestEndDist > dist) {
5381 bestEndDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005382 sLink[iIndex] = rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005383 eLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005384 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005385 dx = iEnd.fX - oEnd.fX;
5386 dy = iEnd.fY - oEnd.fY;
5387 dist = dx * dx + dy * dy;
5388 if (bestEndDist > dist) {
5389 bestEndDist = dist;
5390 eLink[iIndex] = ~rIndex;
5391 eLink[rIndex] = ~iIndex;
5392 }
5393 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005394 }
5395 rIndex = 0;
5396 bool forward = true;
5397 bool first = true;
5398 const SkPoint* startPtr;
5399 int sIndex = sLink[rIndex];
caryclark@google.com0b7da432012-10-31 19:00:20 +00005400 SkASSERT(sIndex != INT_MAX);
5401 sLink[rIndex] = INT_MAX;
5402 int eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005403 if (sIndex < 0) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005404 eIndex = sLink[~sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005405 sLink[~sIndex] = INT_MAX;
5406 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005407 eIndex = eLink[sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005408 eLink[sIndex] = INT_MAX;
5409 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005410 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005411 do {
5412 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005413 outer = runs[rIndex];
5414 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005415 if (first) {
5416 startPtr = &contour.start();
5417 first = false;
5418 simple.deferredMove(startPtr[0]);
5419 }
5420 const SkPoint* endPtr;
5421 if (forward) {
5422 contour.toPartialForward(simple);
5423 endPtr = &contour.end();
5424 } else {
5425 contour.toPartialBackward(simple);
5426 endPtr = &contour.start();
5427 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005428 if (sIndex == eIndex) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005429 simple.close();
5430 first = forward = true;
5431 break;
5432 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005433 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005434 eIndex = eLink[rIndex];
5435 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005436 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005437 if (eIndex >= 0) {
5438 SkASSERT(sLink[eIndex] == rIndex);
5439 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005440 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005441 SkASSERT(eLink[~eIndex] == ~rIndex);
5442 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005443 }
5444 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005445 eIndex = sLink[rIndex];
5446 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005447 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005448 if (eIndex >= 0) {
5449 SkASSERT(eLink[eIndex] == rIndex);
5450 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005451 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005452 SkASSERT(sLink[~eIndex] == ~rIndex);
5453 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005454 }
5455 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005456 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005457 if (rIndex < 0) {
5458 forward ^= 1;
5459 rIndex = ~rIndex;
5460 }
5461 } while (true);
5462 for (rIndex = 0; rIndex < count; ++rIndex) {
5463 if (sLink[rIndex] != INT_MAX) {
5464 break;
5465 }
5466 }
5467 } while (rIndex < count);
5468 SkASSERT(first);
5469}
5470
5471void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005472 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00005473 result.reset();
5474 result.setFillType(SkPath::kEvenOdd_FillType);
5475 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005476
5477 // turn path into list of segments
5478 SkTArray<Contour> contours;
5479 // FIXME: add self-intersecting cubics' T values to segment
5480 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00005481 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005482 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005483 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005484 Contour** currentPtr = contourList.begin();
5485 if (!currentPtr) {
5486 return;
5487 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005488 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005489 // find all intersections between segments
5490 do {
5491 Contour** nextPtr = currentPtr;
5492 Contour* current = *currentPtr++;
5493 Contour* next;
5494 do {
5495 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005496 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005497 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005498 // eat through coincident edges
caryclark@google.com7ba591e2012-11-20 14:21:54 +00005499 coincidenceCheck(contourList, false, 0);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00005500 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005501 sortSegments(contourList);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005502#if DEBUG_ACTIVE_SPANS
5503 debugShowActiveSpans(contourList);
5504#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005505 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005506 if (builder.xorMask() == kWinding_Mask
5507 ? !bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00005508 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005509 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00005510 SkPath temp;
5511 temp.setFillType(SkPath::kEvenOdd_FillType);
5512 PathWrapper assembled(temp);
5513 assemble(simple, assembled);
5514 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00005515 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005516}
5517